aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/runnables.rs
diff options
context:
space:
mode:
authorBenjamin Coenen <[email protected]>2020-05-21 09:48:42 +0100
committerBenjamin Coenen <[email protected]>2020-05-21 09:48:42 +0100
commitc6143742bd4e625d391ac3ea860be7578ab9f53f (patch)
tree43cce6b63b38ab0497c54df8850d7c23afd70e0a /crates/ra_ide/src/runnables.rs
parentebaa05a4478096aaf3bc2a48d0d171a287422c7c (diff)
add support of feature flag for runnables #4464
Signed-off-by: Benjamin Coenen <[email protected]>
Diffstat (limited to 'crates/ra_ide/src/runnables.rs')
-rw-r--r--crates/ra_ide/src/runnables.rs146
1 files changed, 133 insertions, 13 deletions
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index fa8a9d92c..4f7eb2c5b 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -1,11 +1,11 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::Semantics; 3use hir::{Attrs, HirFileId, InFile, Semantics};
4use itertools::Itertools; 4use itertools::Itertools;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
7 ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner}, 7 ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner},
8 match_ast, SyntaxNode, TextRange, 8 match_ast, SmolStr, SyntaxNode, TextRange,
9}; 9};
10 10
11use crate::FileId; 11use crate::FileId;
@@ -16,6 +16,7 @@ use std::fmt::Display;
16pub struct Runnable { 16pub struct Runnable {
17 pub range: TextRange, 17 pub range: TextRange,
18 pub kind: RunnableKind, 18 pub kind: RunnableKind,
19 pub features_needed: Option<Vec<SmolStr>>,
19} 20}
20 21
21#[derive(Debug)] 22#[derive(Debug)]
@@ -45,20 +46,24 @@ pub enum RunnableKind {
45pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { 46pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
46 let sema = Semantics::new(db); 47 let sema = Semantics::new(db);
47 let source_file = sema.parse(file_id); 48 let source_file = sema.parse(file_id);
48 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i)).collect() 49 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect()
49} 50}
50 51
51fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { 52fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode, file_id: FileId) -> Option<Runnable> {
52 match_ast! { 53 match_ast! {
53 match item { 54 match item {
54 ast::FnDef(it) => runnable_fn(sema, it), 55 ast::FnDef(it) => runnable_fn(sema, it, file_id),
55 ast::Module(it) => runnable_mod(sema, it), 56 ast::Module(it) => runnable_mod(sema, it, file_id),
56 _ => None, 57 _ => None,
57 } 58 }
58 } 59 }
59} 60}
60 61
61fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Runnable> { 62fn runnable_fn(
63 sema: &Semantics<RootDatabase>,
64 fn_def: ast::FnDef,
65 file_id: FileId,
66) -> Option<Runnable> {
62 let name_string = fn_def.name()?.text().to_string(); 67 let name_string = fn_def.name()?.text().to_string();
63 68
64 let kind = if name_string == "main" { 69 let kind = if name_string == "main" {
@@ -89,7 +94,11 @@ fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Run
89 return None; 94 return None;
90 } 95 }
91 }; 96 };
92 Some(Runnable { range: fn_def.syntax().text_range(), kind }) 97
98 let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def));
99 let features_needed = get_features_needed(attrs);
100
101 Some(Runnable { range: fn_def.syntax().text_range(), kind, features_needed })
93} 102}
94 103
95#[derive(Debug)] 104#[derive(Debug)]
@@ -125,7 +134,11 @@ fn has_doc_test(fn_def: &ast::FnDef) -> bool {
125 fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```")) 134 fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```"))
126} 135}
127 136
128fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> { 137fn runnable_mod(
138 sema: &Semantics<RootDatabase>,
139 module: ast::Module,
140 file_id: FileId,
141) -> Option<Runnable> {
129 let has_test_function = module 142 let has_test_function = module
130 .item_list()? 143 .item_list()?
131 .items() 144 .items()
@@ -138,11 +151,34 @@ fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<R
138 return None; 151 return None;
139 } 152 }
140 let range = module.syntax().text_range(); 153 let range = module.syntax().text_range();
141 let module = sema.to_def(&module)?; 154 let module_def = sema.to_def(&module)?;
155
156 let path = module_def
157 .path_to_root(sema.db)
158 .into_iter()
159 .rev()
160 .filter_map(|it| it.name(sema.db))
161 .join("::");
162
163 let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module));
164 let features_needed = get_features_needed(attrs);
142 165
143 let path = 166 Some(Runnable { range, kind: RunnableKind::TestMod { path }, features_needed })
144 module.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::"); 167}
145 Some(Runnable { range, kind: RunnableKind::TestMod { path } }) 168
169fn get_features_needed(attrs: Attrs) -> Option<Vec<SmolStr>> {
170 let cfg_expr = attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree));
171 let features_needed = cfg_expr.fold(vec![], |mut acc, cfg| {
172 if let Some(features_needed) = cfg.minimal_features_needed() {
173 acc.extend(features_needed);
174 }
175 acc
176 });
177 if features_needed.is_empty() {
178 None
179 } else {
180 Some(features_needed)
181 }
146} 182}
147 183
148#[cfg(test)] 184#[cfg(test)]
@@ -174,6 +210,7 @@ mod tests {
174 Runnable { 210 Runnable {
175 range: 1..21, 211 range: 1..21,
176 kind: Bin, 212 kind: Bin,
213 features_needed: None,
177 }, 214 },
178 Runnable { 215 Runnable {
179 range: 22..46, 216 range: 22..46,
@@ -185,6 +222,7 @@ mod tests {
185 ignore: false, 222 ignore: false,
186 }, 223 },
187 }, 224 },
225 features_needed: None,
188 }, 226 },
189 Runnable { 227 Runnable {
190 range: 47..81, 228 range: 47..81,
@@ -196,6 +234,7 @@ mod tests {
196 ignore: true, 234 ignore: true,
197 }, 235 },
198 }, 236 },
237 features_needed: None,
199 }, 238 },
200 ] 239 ]
201 "### 240 "###
@@ -223,6 +262,7 @@ mod tests {
223 Runnable { 262 Runnable {
224 range: 1..21, 263 range: 1..21,
225 kind: Bin, 264 kind: Bin,
265 features_needed: None,
226 }, 266 },
227 Runnable { 267 Runnable {
228 range: 22..64, 268 range: 22..64,
@@ -231,6 +271,7 @@ mod tests {
231 "foo", 271 "foo",
232 ), 272 ),
233 }, 273 },
274 features_needed: None,
234 }, 275 },
235 ] 276 ]
236 "### 277 "###
@@ -258,6 +299,7 @@ mod tests {
258 kind: TestMod { 299 kind: TestMod {
259 path: "test_mod", 300 path: "test_mod",
260 }, 301 },
302 features_needed: None,
261 }, 303 },
262 Runnable { 304 Runnable {
263 range: 28..57, 305 range: 28..57,
@@ -269,6 +311,7 @@ mod tests {
269 ignore: false, 311 ignore: false,
270 }, 312 },
271 }, 313 },
314 features_needed: None,
272 }, 315 },
273 ] 316 ]
274 "### 317 "###
@@ -298,6 +341,7 @@ mod tests {
298 kind: TestMod { 341 kind: TestMod {
299 path: "foo::test_mod", 342 path: "foo::test_mod",
300 }, 343 },
344 features_needed: None,
301 }, 345 },
302 Runnable { 346 Runnable {
303 range: 46..79, 347 range: 46..79,
@@ -309,6 +353,7 @@ mod tests {
309 ignore: false, 353 ignore: false,
310 }, 354 },
311 }, 355 },
356 features_needed: None,
312 }, 357 },
313 ] 358 ]
314 "### 359 "###
@@ -340,6 +385,7 @@ mod tests {
340 kind: TestMod { 385 kind: TestMod {
341 path: "foo::bar::test_mod", 386 path: "foo::bar::test_mod",
342 }, 387 },
388 features_needed: None,
343 }, 389 },
344 Runnable { 390 Runnable {
345 range: 68..105, 391 range: 68..105,
@@ -351,6 +397,80 @@ mod tests {
351 ignore: false, 397 ignore: false,
352 }, 398 },
353 }, 399 },
400 features_needed: None,
401 },
402 ]
403 "###
404 );
405 }
406
407 #[test]
408 fn test_runnables_with_feature() {
409 let (analysis, pos) = analysis_and_position(
410 r#"
411 //- /lib.rs crate:foo cfg:feature=foo
412 <|> //empty
413 #[test]
414 #[cfg(feature = "foo")]
415 fn test_foo1() {}
416 "#,
417 );
418 let runnables = analysis.runnables(pos.file_id).unwrap();
419 assert_debug_snapshot!(&runnables,
420 @r###"
421 [
422 Runnable {
423 range: 1..58,
424 kind: Test {
425 test_id: Name(
426 "test_foo1",
427 ),
428 attr: TestAttr {
429 ignore: false,
430 },
431 },
432 features_needed: Some(
433 [
434 "foo",
435 ],
436 ),
437 },
438 ]
439 "###
440 );
441 }
442
443 #[test]
444 fn test_runnables_with_features() {
445 let (analysis, pos) = analysis_and_position(
446 r#"
447 //- /lib.rs crate:foo cfg:feature=foo,feature=bar
448 <|> //empty
449 #[test]
450 #[cfg(all(feature = "foo", feature = "bar"))]
451 fn test_foo1() {}
452 "#,
453 );
454 let runnables = analysis.runnables(pos.file_id).unwrap();
455 assert_debug_snapshot!(&runnables,
456 @r###"
457 [
458 Runnable {
459 range: 1..80,
460 kind: Test {
461 test_id: Name(
462 "test_foo1",
463 ),
464 attr: TestAttr {
465 ignore: false,
466 },
467 },
468 features_needed: Some(
469 [
470 "foo",
471 "bar",
472 ],
473 ),
354 }, 474 },
355 ] 475 ]
356 "### 476 "###