diff options
Diffstat (limited to 'crates/ra_ide')
-rw-r--r-- | crates/ra_ide/src/runnables.rs | 148 |
1 files changed, 135 insertions, 13 deletions
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 131b8f307..3a3d0b0ac 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 | ||
3 | use hir::{AsAssocItem, Semantics}; | 3 | use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; |
4 | use itertools::Itertools; | 4 | use itertools::Itertools; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use 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 | ||
11 | use crate::FileId; | 11 | use crate::FileId; |
@@ -16,6 +16,7 @@ use std::fmt::Display; | |||
16 | pub struct Runnable { | 16 | pub 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 { | |||
45 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { | 46 | pub(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 | ||
51 | fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { | 52 | fn 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 | ||
61 | fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Runnable> { | 62 | fn 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" { |
@@ -111,7 +116,11 @@ fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Run | |||
111 | return None; | 116 | return None; |
112 | } | 117 | } |
113 | }; | 118 | }; |
114 | Some(Runnable { range: fn_def.syntax().text_range(), kind }) | 119 | |
120 | 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 | |||
123 | Some(Runnable { range: fn_def.syntax().text_range(), kind, features_needed }) | ||
115 | } | 124 | } |
116 | 125 | ||
117 | #[derive(Debug)] | 126 | #[derive(Debug)] |
@@ -147,7 +156,11 @@ fn has_doc_test(fn_def: &ast::FnDef) -> bool { | |||
147 | fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```")) | 156 | fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```")) |
148 | } | 157 | } |
149 | 158 | ||
150 | fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> { | 159 | fn runnable_mod( |
160 | sema: &Semantics<RootDatabase>, | ||
161 | module: ast::Module, | ||
162 | file_id: FileId, | ||
163 | ) -> Option<Runnable> { | ||
151 | let has_test_function = module | 164 | let has_test_function = module |
152 | .item_list()? | 165 | .item_list()? |
153 | .items() | 166 | .items() |
@@ -160,11 +173,34 @@ fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<R | |||
160 | return None; | 173 | return None; |
161 | } | 174 | } |
162 | let range = module.syntax().text_range(); | 175 | let range = module.syntax().text_range(); |
163 | let module = sema.to_def(&module)?; | 176 | let module_def = sema.to_def(&module)?; |
177 | |||
178 | let path = module_def | ||
179 | .path_to_root(sema.db) | ||
180 | .into_iter() | ||
181 | .rev() | ||
182 | .filter_map(|it| it.name(sema.db)) | ||
183 | .join("::"); | ||
184 | |||
185 | let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module)); | ||
186 | let features_needed = get_features_needed(attrs); | ||
164 | 187 | ||
165 | let path = | 188 | Some(Runnable { range, kind: RunnableKind::TestMod { path }, features_needed }) |
166 | module.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::"); | 189 | } |
167 | Some(Runnable { range, kind: RunnableKind::TestMod { path } }) | 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.fold(vec![], |mut acc, cfg| { | ||
194 | if let Some(features_needed) = cfg.minimal_features_needed() { | ||
195 | acc.extend(features_needed); | ||
196 | } | ||
197 | acc | ||
198 | }); | ||
199 | if features_needed.is_empty() { | ||
200 | None | ||
201 | } else { | ||
202 | Some(features_needed) | ||
203 | } | ||
168 | } | 204 | } |
169 | 205 | ||
170 | #[cfg(test)] | 206 | #[cfg(test)] |
@@ -196,6 +232,7 @@ mod tests { | |||
196 | Runnable { | 232 | Runnable { |
197 | range: 1..21, | 233 | range: 1..21, |
198 | kind: Bin, | 234 | kind: Bin, |
235 | features_needed: None, | ||
199 | }, | 236 | }, |
200 | Runnable { | 237 | Runnable { |
201 | range: 22..46, | 238 | range: 22..46, |
@@ -207,6 +244,7 @@ mod tests { | |||
207 | ignore: false, | 244 | ignore: false, |
208 | }, | 245 | }, |
209 | }, | 246 | }, |
247 | features_needed: None, | ||
210 | }, | 248 | }, |
211 | Runnable { | 249 | Runnable { |
212 | range: 47..81, | 250 | range: 47..81, |
@@ -218,6 +256,7 @@ mod tests { | |||
218 | ignore: true, | 256 | ignore: true, |
219 | }, | 257 | }, |
220 | }, | 258 | }, |
259 | features_needed: None, | ||
221 | }, | 260 | }, |
222 | ] | 261 | ] |
223 | "### | 262 | "### |
@@ -245,6 +284,7 @@ mod tests { | |||
245 | Runnable { | 284 | Runnable { |
246 | range: 1..21, | 285 | range: 1..21, |
247 | kind: Bin, | 286 | kind: Bin, |
287 | features_needed: None, | ||
248 | }, | 288 | }, |
249 | Runnable { | 289 | Runnable { |
250 | range: 22..64, | 290 | range: 22..64, |
@@ -253,6 +293,7 @@ mod tests { | |||
253 | "foo", | 293 | "foo", |
254 | ), | 294 | ), |
255 | }, | 295 | }, |
296 | features_needed: None, | ||
256 | }, | 297 | }, |
257 | ] | 298 | ] |
258 | "### | 299 | "### |
@@ -283,6 +324,7 @@ mod tests { | |||
283 | Runnable { | 324 | Runnable { |
284 | range: 1..21, | 325 | range: 1..21, |
285 | kind: Bin, | 326 | kind: Bin, |
327 | features_needed: None, | ||
286 | }, | 328 | }, |
287 | Runnable { | 329 | Runnable { |
288 | range: 51..105, | 330 | range: 51..105, |
@@ -291,6 +333,7 @@ mod tests { | |||
291 | "Data::foo", | 333 | "Data::foo", |
292 | ), | 334 | ), |
293 | }, | 335 | }, |
336 | features_needed: None, | ||
294 | }, | 337 | }, |
295 | ] | 338 | ] |
296 | "### | 339 | "### |
@@ -318,6 +361,7 @@ mod tests { | |||
318 | kind: TestMod { | 361 | kind: TestMod { |
319 | path: "test_mod", | 362 | path: "test_mod", |
320 | }, | 363 | }, |
364 | features_needed: None, | ||
321 | }, | 365 | }, |
322 | Runnable { | 366 | Runnable { |
323 | range: 28..57, | 367 | range: 28..57, |
@@ -329,6 +373,7 @@ mod tests { | |||
329 | ignore: false, | 373 | ignore: false, |
330 | }, | 374 | }, |
331 | }, | 375 | }, |
376 | features_needed: None, | ||
332 | }, | 377 | }, |
333 | ] | 378 | ] |
334 | "### | 379 | "### |
@@ -358,6 +403,7 @@ mod tests { | |||
358 | kind: TestMod { | 403 | kind: TestMod { |
359 | path: "foo::test_mod", | 404 | path: "foo::test_mod", |
360 | }, | 405 | }, |
406 | features_needed: None, | ||
361 | }, | 407 | }, |
362 | Runnable { | 408 | Runnable { |
363 | range: 46..79, | 409 | range: 46..79, |
@@ -369,6 +415,7 @@ mod tests { | |||
369 | ignore: false, | 415 | ignore: false, |
370 | }, | 416 | }, |
371 | }, | 417 | }, |
418 | features_needed: None, | ||
372 | }, | 419 | }, |
373 | ] | 420 | ] |
374 | "### | 421 | "### |
@@ -400,6 +447,7 @@ mod tests { | |||
400 | kind: TestMod { | 447 | kind: TestMod { |
401 | path: "foo::bar::test_mod", | 448 | path: "foo::bar::test_mod", |
402 | }, | 449 | }, |
450 | features_needed: None, | ||
403 | }, | 451 | }, |
404 | Runnable { | 452 | Runnable { |
405 | range: 68..105, | 453 | range: 68..105, |
@@ -411,6 +459,80 @@ mod tests { | |||
411 | ignore: false, | 459 | ignore: false, |
412 | }, | 460 | }, |
413 | }, | 461 | }, |
462 | features_needed: None, | ||
463 | }, | ||
464 | ] | ||
465 | "### | ||
466 | ); | ||
467 | } | ||
468 | |||
469 | #[test] | ||
470 | fn test_runnables_with_feature() { | ||
471 | let (analysis, pos) = analysis_and_position( | ||
472 | r#" | ||
473 | //- /lib.rs crate:foo cfg:feature=foo | ||
474 | <|> //empty | ||
475 | #[test] | ||
476 | #[cfg(feature = "foo")] | ||
477 | fn test_foo1() {} | ||
478 | "#, | ||
479 | ); | ||
480 | let runnables = analysis.runnables(pos.file_id).unwrap(); | ||
481 | assert_debug_snapshot!(&runnables, | ||
482 | @r###" | ||
483 | [ | ||
484 | Runnable { | ||
485 | range: 1..58, | ||
486 | kind: Test { | ||
487 | test_id: Name( | ||
488 | "test_foo1", | ||
489 | ), | ||
490 | attr: TestAttr { | ||
491 | ignore: false, | ||
492 | }, | ||
493 | }, | ||
494 | features_needed: Some( | ||
495 | [ | ||
496 | "foo", | ||
497 | ], | ||
498 | ), | ||
499 | }, | ||
500 | ] | ||
501 | "### | ||
502 | ); | ||
503 | } | ||
504 | |||
505 | #[test] | ||
506 | fn test_runnables_with_features() { | ||
507 | let (analysis, pos) = analysis_and_position( | ||
508 | r#" | ||
509 | //- /lib.rs crate:foo cfg:feature=foo,feature=bar | ||
510 | <|> //empty | ||
511 | #[test] | ||
512 | #[cfg(all(feature = "foo", feature = "bar"))] | ||
513 | fn test_foo1() {} | ||
514 | "#, | ||
515 | ); | ||
516 | let runnables = analysis.runnables(pos.file_id).unwrap(); | ||
517 | assert_debug_snapshot!(&runnables, | ||
518 | @r###" | ||
519 | [ | ||
520 | Runnable { | ||
521 | range: 1..80, | ||
522 | kind: Test { | ||
523 | test_id: Name( | ||
524 | "test_foo1", | ||
525 | ), | ||
526 | attr: TestAttr { | ||
527 | ignore: false, | ||
528 | }, | ||
529 | }, | ||
530 | features_needed: Some( | ||
531 | [ | ||
532 | "foo", | ||
533 | "bar", | ||
534 | ], | ||
535 | ), | ||
414 | }, | 536 | }, |
415 | ] | 537 | ] |
416 | "### | 538 | "### |