diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_cfg/src/cfg_expr.rs | 55 | ||||
-rw-r--r-- | crates/ra_ide/src/runnables.rs | 76 | ||||
-rw-r--r-- | crates/rust-analyzer/Cargo.toml | 3 | ||||
-rw-r--r-- | crates/rust-analyzer/src/cargo_target_spec.rs | 13 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop/handlers.rs | 94 |
5 files changed, 142 insertions, 99 deletions
diff --git a/crates/ra_cfg/src/cfg_expr.rs b/crates/ra_cfg/src/cfg_expr.rs index 98f44f56d..85b100c6a 100644 --- a/crates/ra_cfg/src/cfg_expr.rs +++ b/crates/ra_cfg/src/cfg_expr.rs | |||
@@ -33,33 +33,6 @@ 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) -> Vec<SmolStr> { | ||
39 | let mut features = vec![]; | ||
40 | self.collect_minimal_features_needed(&mut features); | ||
41 | |||
42 | features | ||
43 | } | ||
44 | |||
45 | fn collect_minimal_features_needed(&self, features: &mut Vec<SmolStr>) { | ||
46 | match self { | ||
47 | CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.clone()), | ||
48 | CfgExpr::All(preds) => { | ||
49 | preds.iter().for_each(|cfg| cfg.collect_minimal_features_needed(features)); | ||
50 | } | ||
51 | CfgExpr::Any(preds) => { | ||
52 | for cfg in preds { | ||
53 | let len_features = features.len(); | ||
54 | cfg.collect_minimal_features_needed(features); | ||
55 | if len_features != features.len() { | ||
56 | break; | ||
57 | } | ||
58 | } | ||
59 | } | ||
60 | _ => {} | ||
61 | } | ||
62 | } | ||
63 | } | 36 | } |
64 | 37 | ||
65 | pub fn parse_cfg(tt: &Subtree) -> CfgExpr { | 38 | pub fn parse_cfg(tt: &Subtree) -> CfgExpr { |
@@ -160,32 +133,4 @@ mod tests { | |||
160 | ]), | 133 | ]), |
161 | ); | 134 | ); |
162 | } | 135 | } |
163 | |||
164 | #[test] | ||
165 | fn test_cfg_expr_minimal_features_needed() { | ||
166 | let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#); | ||
167 | let cfg_expr = parse_cfg(&subtree); | ||
168 | |||
169 | assert_eq!(cfg_expr.minimal_features_needed(), vec![SmolStr::new("baz")]); | ||
170 | |||
171 | let (subtree, _) = | ||
172 | get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#); | ||
173 | let cfg_expr = parse_cfg(&subtree); | ||
174 | |||
175 | assert_eq!( | ||
176 | cfg_expr.minimal_features_needed(), | ||
177 | vec![SmolStr::new("baz"), SmolStr::new("foo")] | ||
178 | ); | ||
179 | |||
180 | let (subtree, _) = | ||
181 | get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#); | ||
182 | let cfg_expr = parse_cfg(&subtree); | ||
183 | |||
184 | assert_eq!(cfg_expr.minimal_features_needed(), vec![SmolStr::new("baz")]); | ||
185 | |||
186 | let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#); | ||
187 | let cfg_expr = parse_cfg(&subtree); | ||
188 | |||
189 | assert!(cfg_expr.minimal_features_needed().is_empty()); | ||
190 | } | ||
191 | } | 136 | } |
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index a460370c5..a96c5f157 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs | |||
@@ -10,13 +10,14 @@ use ra_syntax::{ | |||
10 | 10 | ||
11 | use crate::FileId; | 11 | use crate::FileId; |
12 | use ast::DocCommentsOwner; | 12 | use ast::DocCommentsOwner; |
13 | use ra_cfg::CfgExpr; | ||
13 | use std::fmt::Display; | 14 | use std::fmt::Display; |
14 | 15 | ||
15 | #[derive(Debug)] | 16 | #[derive(Debug)] |
16 | pub struct Runnable { | 17 | pub struct Runnable { |
17 | pub range: TextRange, | 18 | pub range: TextRange, |
18 | pub kind: RunnableKind, | 19 | pub kind: RunnableKind, |
19 | pub features_needed: Option<Vec<SmolStr>>, | 20 | pub cfg_exprs: Vec<CfgExpr>, |
20 | } | 21 | } |
21 | 22 | ||
22 | #[derive(Debug)] | 23 | #[derive(Debug)] |
@@ -118,9 +119,10 @@ fn runnable_fn( | |||
118 | }; | 119 | }; |
119 | 120 | ||
120 | let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def)); | 121 | let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def)); |
121 | let features_needed = get_features_needed(attrs); | 122 | let cfg_exprs = |
123 | attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); | ||
122 | 124 | ||
123 | Some(Runnable { range: fn_def.syntax().text_range(), kind, features_needed }) | 125 | Some(Runnable { range: fn_def.syntax().text_range(), kind, cfg_exprs }) |
124 | } | 126 | } |
125 | 127 | ||
126 | #[derive(Debug)] | 128 | #[derive(Debug)] |
@@ -183,15 +185,10 @@ fn runnable_mod( | |||
183 | .join("::"); | 185 | .join("::"); |
184 | 186 | ||
185 | let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module)); | 187 | let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module)); |
186 | let features_needed = get_features_needed(attrs); | 188 | let cfg_exprs = |
189 | attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); | ||
187 | 190 | ||
188 | Some(Runnable { range, kind: RunnableKind::TestMod { path }, features_needed }) | 191 | Some(Runnable { range, kind: RunnableKind::TestMod { path }, cfg_exprs }) |
189 | } | ||
190 | |||
191 | fn get_features_needed(attrs: Attrs) -> Option<Vec<SmolStr>> { | ||
192 | let cfg_expr = attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)); | ||
193 | let features_needed = cfg_expr.map(|cfg| cfg.minimal_features_needed()).flatten().collect(); | ||
194 | Some(features_needed).filter(|it: &Vec<SmolStr>| !it.is_empty()) | ||
195 | } | 192 | } |
196 | 193 | ||
197 | #[cfg(test)] | 194 | #[cfg(test)] |
@@ -223,7 +220,7 @@ mod tests { | |||
223 | Runnable { | 220 | Runnable { |
224 | range: 1..21, | 221 | range: 1..21, |
225 | kind: Bin, | 222 | kind: Bin, |
226 | features_needed: None, | 223 | cfg_exprs: [], |
227 | }, | 224 | }, |
228 | Runnable { | 225 | Runnable { |
229 | range: 22..46, | 226 | range: 22..46, |
@@ -235,7 +232,7 @@ mod tests { | |||
235 | ignore: false, | 232 | ignore: false, |
236 | }, | 233 | }, |
237 | }, | 234 | }, |
238 | features_needed: None, | 235 | cfg_exprs: [], |
239 | }, | 236 | }, |
240 | Runnable { | 237 | Runnable { |
241 | range: 47..81, | 238 | range: 47..81, |
@@ -247,7 +244,7 @@ mod tests { | |||
247 | ignore: true, | 244 | ignore: true, |
248 | }, | 245 | }, |
249 | }, | 246 | }, |
250 | features_needed: None, | 247 | cfg_exprs: [], |
251 | }, | 248 | }, |
252 | ] | 249 | ] |
253 | "### | 250 | "### |
@@ -275,7 +272,7 @@ mod tests { | |||
275 | Runnable { | 272 | Runnable { |
276 | range: 1..21, | 273 | range: 1..21, |
277 | kind: Bin, | 274 | kind: Bin, |
278 | features_needed: None, | 275 | cfg_exprs: [], |
279 | }, | 276 | }, |
280 | Runnable { | 277 | Runnable { |
281 | range: 22..64, | 278 | range: 22..64, |
@@ -284,7 +281,7 @@ mod tests { | |||
284 | "foo", | 281 | "foo", |
285 | ), | 282 | ), |
286 | }, | 283 | }, |
287 | features_needed: None, | 284 | cfg_exprs: [], |
288 | }, | 285 | }, |
289 | ] | 286 | ] |
290 | "### | 287 | "### |
@@ -315,7 +312,7 @@ mod tests { | |||
315 | Runnable { | 312 | Runnable { |
316 | range: 1..21, | 313 | range: 1..21, |
317 | kind: Bin, | 314 | kind: Bin, |
318 | features_needed: None, | 315 | cfg_exprs: [], |
319 | }, | 316 | }, |
320 | Runnable { | 317 | Runnable { |
321 | range: 51..105, | 318 | range: 51..105, |
@@ -324,7 +321,7 @@ mod tests { | |||
324 | "Data::foo", | 321 | "Data::foo", |
325 | ), | 322 | ), |
326 | }, | 323 | }, |
327 | features_needed: None, | 324 | cfg_exprs: [], |
328 | }, | 325 | }, |
329 | ] | 326 | ] |
330 | "### | 327 | "### |
@@ -352,7 +349,7 @@ mod tests { | |||
352 | kind: TestMod { | 349 | kind: TestMod { |
353 | path: "test_mod", | 350 | path: "test_mod", |
354 | }, | 351 | }, |
355 | features_needed: None, | 352 | cfg_exprs: [], |
356 | }, | 353 | }, |
357 | Runnable { | 354 | Runnable { |
358 | range: 28..57, | 355 | range: 28..57, |
@@ -364,7 +361,7 @@ mod tests { | |||
364 | ignore: false, | 361 | ignore: false, |
365 | }, | 362 | }, |
366 | }, | 363 | }, |
367 | features_needed: None, | 364 | cfg_exprs: [], |
368 | }, | 365 | }, |
369 | ] | 366 | ] |
370 | "### | 367 | "### |
@@ -394,7 +391,7 @@ mod tests { | |||
394 | kind: TestMod { | 391 | kind: TestMod { |
395 | path: "foo::test_mod", | 392 | path: "foo::test_mod", |
396 | }, | 393 | }, |
397 | features_needed: None, | 394 | cfg_exprs: [], |
398 | }, | 395 | }, |
399 | Runnable { | 396 | Runnable { |
400 | range: 46..79, | 397 | range: 46..79, |
@@ -406,7 +403,7 @@ mod tests { | |||
406 | ignore: false, | 403 | ignore: false, |
407 | }, | 404 | }, |
408 | }, | 405 | }, |
409 | features_needed: None, | 406 | cfg_exprs: [], |
410 | }, | 407 | }, |
411 | ] | 408 | ] |
412 | "### | 409 | "### |
@@ -438,7 +435,7 @@ mod tests { | |||
438 | kind: TestMod { | 435 | kind: TestMod { |
439 | path: "foo::bar::test_mod", | 436 | path: "foo::bar::test_mod", |
440 | }, | 437 | }, |
441 | features_needed: None, | 438 | cfg_exprs: [], |
442 | }, | 439 | }, |
443 | Runnable { | 440 | Runnable { |
444 | range: 68..105, | 441 | range: 68..105, |
@@ -450,7 +447,7 @@ mod tests { | |||
450 | ignore: false, | 447 | ignore: false, |
451 | }, | 448 | }, |
452 | }, | 449 | }, |
453 | features_needed: None, | 450 | cfg_exprs: [], |
454 | }, | 451 | }, |
455 | ] | 452 | ] |
456 | "### | 453 | "### |
@@ -482,11 +479,12 @@ mod tests { | |||
482 | ignore: false, | 479 | ignore: false, |
483 | }, | 480 | }, |
484 | }, | 481 | }, |
485 | features_needed: Some( | 482 | cfg_exprs: [ |
486 | [ | 483 | KeyValue { |
487 | "foo", | 484 | key: "feature", |
488 | ], | 485 | value: "foo", |
489 | ), | 486 | }, |
487 | ], | ||
490 | }, | 488 | }, |
491 | ] | 489 | ] |
492 | "### | 490 | "### |
@@ -518,12 +516,20 @@ mod tests { | |||
518 | ignore: false, | 516 | ignore: false, |
519 | }, | 517 | }, |
520 | }, | 518 | }, |
521 | features_needed: Some( | 519 | cfg_exprs: [ |
522 | [ | 520 | All( |
523 | "foo", | 521 | [ |
524 | "bar", | 522 | KeyValue { |
525 | ], | 523 | key: "feature", |
526 | ), | 524 | value: "foo", |
525 | }, | ||
526 | KeyValue { | ||
527 | key: "feature", | ||
528 | value: "bar", | ||
529 | }, | ||
530 | ], | ||
531 | ), | ||
532 | ], | ||
527 | }, | 533 | }, |
528 | ] | 534 | ] |
529 | "### | 535 | "### |
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 9b2d29b1d..65b487db3 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -40,6 +40,7 @@ ra_project_model = { path = "../ra_project_model" } | |||
40 | ra_syntax = { path = "../ra_syntax" } | 40 | ra_syntax = { path = "../ra_syntax" } |
41 | ra_text_edit = { path = "../ra_text_edit" } | 41 | ra_text_edit = { path = "../ra_text_edit" } |
42 | ra_vfs = "0.6.0" | 42 | ra_vfs = "0.6.0" |
43 | ra_cfg = { path = "../ra_cfg"} | ||
43 | 44 | ||
44 | # This should only be used in CLI | 45 | # This should only be used in CLI |
45 | ra_db = { path = "../ra_db" } | 46 | ra_db = { path = "../ra_db" } |
@@ -55,6 +56,8 @@ winapi = "0.3.8" | |||
55 | tempfile = "3.1.0" | 56 | tempfile = "3.1.0" |
56 | insta = "0.16.0" | 57 | insta = "0.16.0" |
57 | test_utils = { path = "../test_utils" } | 58 | test_utils = { path = "../test_utils" } |
59 | mbe = { path = "../ra_mbe", package = "ra_mbe" } | ||
60 | tt = { path = "../ra_tt", package = "ra_tt" } | ||
58 | 61 | ||
59 | [features] | 62 | [features] |
60 | jemalloc = [ "ra_prof/jemalloc" ] | 63 | jemalloc = [ "ra_prof/jemalloc" ] |
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs index a2f85060b..441fb61df 100644 --- a/crates/rust-analyzer/src/cargo_target_spec.rs +++ b/crates/rust-analyzer/src/cargo_target_spec.rs | |||
@@ -21,7 +21,7 @@ impl CargoTargetSpec { | |||
21 | pub(crate) fn runnable_args( | 21 | pub(crate) fn runnable_args( |
22 | spec: Option<CargoTargetSpec>, | 22 | spec: Option<CargoTargetSpec>, |
23 | kind: &RunnableKind, | 23 | kind: &RunnableKind, |
24 | features_needed: &Option<Vec<SmolStr>>, | 24 | features_needed: &Vec<SmolStr>, |
25 | ) -> Result<(Vec<String>, Vec<String>)> { | 25 | ) -> Result<(Vec<String>, Vec<String>)> { |
26 | let mut args = Vec::new(); | 26 | let mut args = Vec::new(); |
27 | let mut extra_args = Vec::new(); | 27 | let mut extra_args = Vec::new(); |
@@ -76,12 +76,11 @@ impl CargoTargetSpec { | |||
76 | } | 76 | } |
77 | } | 77 | } |
78 | 78 | ||
79 | if let Some(features_needed) = features_needed { | 79 | features_needed.iter().for_each(|feature| { |
80 | features_needed.iter().for_each(|feature| { | 80 | args.push("--features".to_string()); |
81 | args.push("--features".to_string()); | 81 | args.push(feature.to_string()); |
82 | args.push(feature.to_string()); | 82 | }); |
83 | }); | 83 | |
84 | } | ||
85 | Ok((args, extra_args)) | 84 | Ok((args, extra_args)) |
86 | } | 85 | } |
87 | 86 | ||
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index cc9abd162..5f6e845a8 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -17,12 +17,13 @@ use lsp_types::{ | |||
17 | SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, | 17 | SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, |
18 | SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, TextEdit, Url, WorkspaceEdit, | 18 | SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, TextEdit, Url, WorkspaceEdit, |
19 | }; | 19 | }; |
20 | use ra_cfg::CfgExpr; | ||
20 | use ra_ide::{ | 21 | use ra_ide::{ |
21 | Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, | 22 | Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, |
22 | }; | 23 | }; |
23 | use ra_prof::profile; | 24 | use ra_prof::profile; |
24 | use ra_project_model::TargetKind; | 25 | use ra_project_model::TargetKind; |
25 | use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; | 26 | use ra_syntax::{AstNode, SmolStr, SyntaxKind, TextRange, TextSize}; |
26 | use rustc_hash::FxHashMap; | 27 | use rustc_hash::FxHashMap; |
27 | use serde::{Deserialize, Serialize}; | 28 | use serde::{Deserialize, Serialize}; |
28 | use serde_json::to_value; | 29 | use serde_json::to_value; |
@@ -38,6 +39,7 @@ use crate::{ | |||
38 | world::WorldSnapshot, | 39 | world::WorldSnapshot, |
39 | LspError, Result, | 40 | LspError, Result, |
40 | }; | 41 | }; |
42 | use hir::Attrs; | ||
41 | 43 | ||
42 | pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { | 44 | pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { |
43 | let _p = profile("handle_analyzer_status"); | 45 | let _p = profile("handle_analyzer_status"); |
@@ -1006,8 +1008,12 @@ fn to_lsp_runnable( | |||
1006 | ) -> Result<lsp_ext::Runnable> { | 1008 | ) -> Result<lsp_ext::Runnable> { |
1007 | let spec = CargoTargetSpec::for_file(world, file_id)?; | 1009 | let spec = CargoTargetSpec::for_file(world, file_id)?; |
1008 | let target = spec.as_ref().map(|s| s.target.clone()); | 1010 | let target = spec.as_ref().map(|s| s.target.clone()); |
1011 | let mut features_needed = vec![]; | ||
1012 | for cfg_expr in &runnable.cfg_exprs { | ||
1013 | collect_minimal_features_needed(cfg_expr, &mut features_needed); | ||
1014 | } | ||
1009 | let (args, extra_args) = | 1015 | let (args, extra_args) = |
1010 | CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.features_needed)?; | 1016 | CargoTargetSpec::runnable_args(spec, &runnable.kind, &features_needed)?; |
1011 | let line_index = world.analysis().file_line_index(file_id)?; | 1017 | let line_index = world.analysis().file_line_index(file_id)?; |
1012 | let label = match &runnable.kind { | 1018 | let label = match &runnable.kind { |
1013 | RunnableKind::Test { test_id, .. } => format!("test {}", test_id), | 1019 | RunnableKind::Test { test_id, .. } => format!("test {}", test_id), |
@@ -1033,6 +1039,39 @@ fn to_lsp_runnable( | |||
1033 | }) | 1039 | }) |
1034 | } | 1040 | } |
1035 | 1041 | ||
1042 | fn get_features_needed(attrs: Attrs) -> Option<Vec<SmolStr>> { | ||
1043 | let cfg_expr = attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)); | ||
1044 | let features_needed = cfg_expr | ||
1045 | .map(|cfg| { | ||
1046 | let mut min_features = vec![]; | ||
1047 | collect_minimal_features_needed(&cfg, &mut min_features); | ||
1048 | min_features | ||
1049 | }) | ||
1050 | .flatten() | ||
1051 | .collect(); | ||
1052 | Some(features_needed).filter(|it: &Vec<SmolStr>| !it.is_empty()) | ||
1053 | } | ||
1054 | |||
1055 | /// Fill minimal features needed | ||
1056 | fn collect_minimal_features_needed(cfg_expr: &CfgExpr, features: &mut Vec<SmolStr>) { | ||
1057 | match cfg_expr { | ||
1058 | CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.clone()), | ||
1059 | CfgExpr::All(preds) => { | ||
1060 | preds.iter().for_each(|cfg| collect_minimal_features_needed(cfg, features)); | ||
1061 | } | ||
1062 | CfgExpr::Any(preds) => { | ||
1063 | for cfg in preds { | ||
1064 | let len_features = features.len(); | ||
1065 | collect_minimal_features_needed(cfg, features); | ||
1066 | if len_features != features.len() { | ||
1067 | break; | ||
1068 | } | ||
1069 | } | ||
1070 | } | ||
1071 | _ => {} | ||
1072 | } | ||
1073 | } | ||
1074 | |||
1036 | pub fn handle_inlay_hints( | 1075 | pub fn handle_inlay_hints( |
1037 | world: WorldSnapshot, | 1076 | world: WorldSnapshot, |
1038 | params: InlayHintsParams, | 1077 | params: InlayHintsParams, |
@@ -1169,3 +1208,54 @@ pub fn handle_semantic_tokens_range( | |||
1169 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); | 1208 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); |
1170 | Ok(Some(semantic_tokens.into())) | 1209 | Ok(Some(semantic_tokens.into())) |
1171 | } | 1210 | } |
1211 | |||
1212 | #[cfg(test)] | ||
1213 | mod tests { | ||
1214 | use super::*; | ||
1215 | |||
1216 | use mbe::{ast_to_token_tree, TokenMap}; | ||
1217 | use ra_cfg::parse_cfg; | ||
1218 | use ra_syntax::{ | ||
1219 | ast::{self, AstNode}, | ||
1220 | SmolStr, | ||
1221 | }; | ||
1222 | |||
1223 | fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) { | ||
1224 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
1225 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
1226 | ast_to_token_tree(&tt).unwrap() | ||
1227 | } | ||
1228 | |||
1229 | #[test] | ||
1230 | fn test_cfg_expr_minimal_features_needed() { | ||
1231 | let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#); | ||
1232 | let cfg_expr = parse_cfg(&subtree); | ||
1233 | let mut min_features = vec![]; | ||
1234 | collect_minimal_features_needed(&cfg_expr, &mut min_features); | ||
1235 | |||
1236 | assert_eq!(min_features, vec![SmolStr::new("baz")]); | ||
1237 | |||
1238 | let (subtree, _) = | ||
1239 | get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#); | ||
1240 | let cfg_expr = parse_cfg(&subtree); | ||
1241 | |||
1242 | let mut min_features = vec![]; | ||
1243 | collect_minimal_features_needed(&cfg_expr, &mut min_features); | ||
1244 | assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]); | ||
1245 | |||
1246 | let (subtree, _) = | ||
1247 | get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#); | ||
1248 | let cfg_expr = parse_cfg(&subtree); | ||
1249 | |||
1250 | let mut min_features = vec![]; | ||
1251 | collect_minimal_features_needed(&cfg_expr, &mut min_features); | ||
1252 | assert_eq!(min_features, vec![SmolStr::new("baz")]); | ||
1253 | |||
1254 | let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#); | ||
1255 | let cfg_expr = parse_cfg(&subtree); | ||
1256 | |||
1257 | let mut min_features = vec![]; | ||
1258 | collect_minimal_features_needed(&cfg_expr, &mut min_features); | ||
1259 | assert!(min_features.is_empty()); | ||
1260 | } | ||
1261 | } | ||