aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_cfg
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_cfg')
-rw-r--r--crates/ra_cfg/src/cfg_expr.rs68
1 files changed, 65 insertions, 3 deletions
diff --git a/crates/ra_cfg/src/cfg_expr.rs b/crates/ra_cfg/src/cfg_expr.rs
index 39d71851c..a4b201e0e 100644
--- a/crates/ra_cfg/src/cfg_expr.rs
+++ b/crates/ra_cfg/src/cfg_expr.rs
@@ -33,6 +33,36 @@ impl CfgExpr {
33 CfgExpr::Not(pred) => pred.fold(query).map(|s| !s), 33 CfgExpr::Not(pred) => pred.fold(query).map(|s| !s),
34 } 34 }
35 } 35 }
36
37 /// Return minimal features needed
38 pub fn minimal_features_needed(&self) -> Option<Vec<SmolStr>> {
39 let mut features = vec![];
40 self.collect_minimal_features_needed(&mut features);
41 if features.is_empty() {
42 None
43 } else {
44 Some(features)
45 }
46 }
47
48 fn collect_minimal_features_needed(&self, features: &mut Vec<SmolStr>) {
49 match self {
50 CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.clone()),
51 CfgExpr::All(preds) => {
52 preds.iter().for_each(|cfg| cfg.collect_minimal_features_needed(features));
53 }
54 CfgExpr::Any(preds) => {
55 for cfg in preds {
56 let len_features = features.len();
57 cfg.collect_minimal_features_needed(features);
58 if len_features != features.len() {
59 break;
60 }
61 }
62 }
63 _ => {}
64 }
65 }
36} 66}
37 67
38pub fn parse_cfg(tt: &Subtree) -> CfgExpr { 68pub fn parse_cfg(tt: &Subtree) -> CfgExpr {
@@ -88,13 +118,17 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> {
88mod tests { 118mod tests {
89 use super::*; 119 use super::*;
90 120
91 use mbe::ast_to_token_tree; 121 use mbe::{ast_to_token_tree, TokenMap};
92 use ra_syntax::ast::{self, AstNode}; 122 use ra_syntax::ast::{self, AstNode};
93 123
94 fn assert_parse_result(input: &str, expected: CfgExpr) { 124 fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) {
95 let source_file = ast::SourceFile::parse(input).ok().unwrap(); 125 let source_file = ast::SourceFile::parse(input).ok().unwrap();
96 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); 126 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
97 let (tt, _) = ast_to_token_tree(&tt).unwrap(); 127 ast_to_token_tree(&tt).unwrap()
128 }
129
130 fn assert_parse_result(input: &str, expected: CfgExpr) {
131 let (tt, _) = get_token_tree_generated(input);
98 assert_eq!(parse_cfg(&tt), expected); 132 assert_eq!(parse_cfg(&tt), expected);
99 } 133 }
100 134
@@ -129,4 +163,32 @@ mod tests {
129 ]), 163 ]),
130 ); 164 );
131 } 165 }
166
167 #[test]
168 fn test_cfg_expr_minimal_features_needed() {
169 let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#);
170 let cfg_expr = parse_cfg(&subtree);
171
172 assert_eq!(cfg_expr.minimal_features_needed().unwrap(), vec![SmolStr::new("baz")]);
173
174 let (subtree, _) =
175 get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#);
176 let cfg_expr = parse_cfg(&subtree);
177
178 assert_eq!(
179 cfg_expr.minimal_features_needed().unwrap(),
180 vec![SmolStr::new("baz"), SmolStr::new("foo")]
181 );
182
183 let (subtree, _) =
184 get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#);
185 let cfg_expr = parse_cfg(&subtree);
186
187 assert_eq!(cfg_expr.minimal_features_needed().unwrap(), vec![SmolStr::new("baz")]);
188
189 let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#);
190 let cfg_expr = parse_cfg(&subtree);
191
192 assert!(cfg_expr.minimal_features_needed().is_none());
193 }
132} 194}