aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/runnables.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src/runnables.rs')
-rw-r--r--crates/ide/src/runnables.rs128
1 files changed, 71 insertions, 57 deletions
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index f4030f3ef..f5ee7de86 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -3,7 +3,7 @@ use std::fmt;
3use assists::utils::test_related_attribute; 3use assists::utils::test_related_attribute;
4use cfg::CfgExpr; 4use cfg::CfgExpr;
5use hir::{AsAssocItem, HasAttrs, HasSource, Semantics}; 5use hir::{AsAssocItem, HasAttrs, HasSource, Semantics};
6use ide_db::RootDatabase; 6use ide_db::{defs::Definition, RootDatabase};
7use itertools::Itertools; 7use itertools::Itertools;
8use syntax::{ 8use syntax::{
9 ast::{self, AstNode, AttrsOwner, ModuleItemOwner}, 9 ast::{self, AstNode, AttrsOwner, ModuleItemOwner},
@@ -96,21 +96,26 @@ impl Runnable {
96pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { 96pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
97 let sema = Semantics::new(db); 97 let sema = Semantics::new(db);
98 let source_file = sema.parse(file_id); 98 let source_file = sema.parse(file_id);
99 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i)).collect() 99 source_file
100} 100 .syntax()
101 101 .descendants()
102pub(crate) fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { 102 .filter_map(|item| {
103 let runnable_item = match_ast! { 103 let runnable = match_ast! {
104 match (item.clone()) { 104 match item {
105 ast::Fn(func) => { 105 ast::Fn(func) => {
106 let def = sema.to_def(&func)?; 106 let def = sema.to_def(&func)?;
107 runnable_fn(sema, def) 107 runnable_fn(&sema, def)
108 }, 108 },
109 ast::Module(it) => runnable_mod(sema, it), 109 ast::Module(it) => runnable_mod(&sema, it),
110 _ => None, 110 _ => None,
111 } 111 }
112 }; 112 };
113 runnable_item.or_else(|| runnable_doctest(sema, item)) 113 runnable.or_else(|| match doc_owner_to_def(&sema, item)? {
114 Definition::ModuleDef(def) => module_def_doctest(&sema, def),
115 _ => None,
116 })
117 })
118 .collect()
114} 119}
115 120
116pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> { 121pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> {
@@ -145,20 +150,49 @@ pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) ->
145 Some(Runnable { nav, kind, cfg }) 150 Some(Runnable { nav, kind, cfg })
146} 151}
147 152
148fn runnable_doctest(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { 153pub(crate) fn runnable_mod(
149 match_ast! { 154 sema: &Semantics<RootDatabase>,
155 module: ast::Module,
156) -> Option<Runnable> {
157 if !has_test_function_or_multiple_test_submodules(&module) {
158 return None;
159 }
160 let module_def = sema.to_def(&module)?;
161
162 let path = module_def
163 .path_to_root(sema.db)
164 .into_iter()
165 .rev()
166 .filter_map(|it| it.name(sema.db))
167 .join("::");
168
169 let def = sema.to_def(&module)?;
170 let attrs = def.attrs(sema.db);
171 let cfg = attrs.cfg();
172 let nav = module_def.to_nav(sema.db);
173 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg })
174}
175
176// FIXME: figure out a proper API here.
177pub(crate) fn doc_owner_to_def(
178 sema: &Semantics<RootDatabase>,
179 item: SyntaxNode,
180) -> Option<Definition> {
181 let res: hir::ModuleDef = match_ast! {
150 match item { 182 match item {
151 ast::Fn(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 183 ast::SourceFile(it) => sema.scope(&item).module()?.into(),
152 ast::Struct(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 184 ast::Fn(it) => sema.to_def(&it)?.into(),
153 ast::Enum(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 185 ast::Struct(it) => sema.to_def(&it)?.into(),
154 ast::Union(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 186 ast::Enum(it) => sema.to_def(&it)?.into(),
155 ast::Trait(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 187 ast::Union(it) => sema.to_def(&it)?.into(),
156 ast::Const(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 188 ast::Trait(it) => sema.to_def(&it)?.into(),
157 ast::Static(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 189 ast::Const(it) => sema.to_def(&it)?.into(),
158 ast::TypeAlias(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 190 ast::Static(it) => sema.to_def(&it)?.into(),
159 _ => None, 191 ast::TypeAlias(it) => sema.to_def(&it)?.into(),
192 _ => return None,
160 } 193 }
161 } 194 };
195 Some(Definition::ModuleDef(res))
162} 196}
163 197
164fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Option<Runnable> { 198fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Option<Runnable> {
@@ -253,26 +287,6 @@ fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
253 }) 287 })
254} 288}
255 289
256fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> {
257 if !has_test_function_or_multiple_test_submodules(&module) {
258 return None;
259 }
260 let module_def = sema.to_def(&module)?;
261
262 let path = module_def
263 .path_to_root(sema.db)
264 .into_iter()
265 .rev()
266 .filter_map(|it| it.name(sema.db))
267 .join("::");
268
269 let def = sema.to_def(&module)?;
270 let attrs = def.attrs(sema.db);
271 let cfg = attrs.cfg();
272 let nav = module_def.to_nav(sema.db);
273 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg })
274}
275
276// We could create runnables for modules with number_of_test_submodules > 0, 290// We could create runnables for modules with number_of_test_submodules > 0,
277// but that bloats the runnables for no real benefit, since all tests can be run by the submodule already 291// but that bloats the runnables for no real benefit, since all tests can be run by the submodule already
278fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool { 292fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool {
@@ -329,7 +343,7 @@ mod tests {
329 check( 343 check(
330 r#" 344 r#"
331//- /lib.rs 345//- /lib.rs
332<|> 346$0
333fn main() {} 347fn main() {}
334 348
335#[test] 349#[test]
@@ -425,7 +439,7 @@ fn bench() {}
425 check( 439 check(
426 r#" 440 r#"
427//- /lib.rs 441//- /lib.rs
428<|> 442$0
429fn main() {} 443fn main() {}
430 444
431/// ``` 445/// ```
@@ -573,7 +587,7 @@ struct StructWithRunnable(String);
573 check( 587 check(
574 r#" 588 r#"
575//- /lib.rs 589//- /lib.rs
576<|> 590$0
577fn main() {} 591fn main() {}
578 592
579struct Data; 593struct Data;
@@ -625,7 +639,7 @@ impl Data {
625 check( 639 check(
626 r#" 640 r#"
627//- /lib.rs 641//- /lib.rs
628<|> 642$0
629mod test_mod { 643mod test_mod {
630 #[test] 644 #[test]
631 fn test_foo1() {} 645 fn test_foo1() {}
@@ -679,7 +693,7 @@ mod test_mod {
679 check( 693 check(
680 r#" 694 r#"
681//- /lib.rs 695//- /lib.rs
682<|> 696$0
683mod root_tests { 697mod root_tests {
684 mod nested_tests_0 { 698 mod nested_tests_0 {
685 mod nested_tests_1 { 699 mod nested_tests_1 {
@@ -819,7 +833,7 @@ mod root_tests {
819 check( 833 check(
820 r#" 834 r#"
821//- /lib.rs crate:foo cfg:feature=foo 835//- /lib.rs crate:foo cfg:feature=foo
822<|> 836$0
823#[test] 837#[test]
824#[cfg(feature = "foo")] 838#[cfg(feature = "foo")]
825fn test_foo1() {} 839fn test_foo1() {}
@@ -864,7 +878,7 @@ fn test_foo1() {}
864 check( 878 check(
865 r#" 879 r#"
866//- /lib.rs crate:foo cfg:feature=foo,feature=bar 880//- /lib.rs crate:foo cfg:feature=foo,feature=bar
867<|> 881$0
868#[test] 882#[test]
869#[cfg(all(feature = "foo", feature = "bar"))] 883#[cfg(all(feature = "foo", feature = "bar"))]
870fn test_foo1() {} 884fn test_foo1() {}
@@ -919,7 +933,7 @@ fn test_foo1() {}
919 check( 933 check(
920 r#" 934 r#"
921//- /lib.rs 935//- /lib.rs
922<|> 936$0
923mod test_mod { 937mod test_mod {
924 fn foo1() {} 938 fn foo1() {}
925} 939}
@@ -938,7 +952,7 @@ mod test_mod {
938//- /lib.rs 952//- /lib.rs
939mod foo; 953mod foo;
940//- /foo.rs 954//- /foo.rs
941struct Foo;<|> 955struct Foo;$0
942impl Foo { 956impl Foo {
943 /// ``` 957 /// ```
944 /// let x = 5; 958 /// let x = 5;