diff options
Diffstat (limited to 'crates/ide/src/runnables.rs')
-rw-r--r-- | crates/ide/src/runnables.rs | 128 |
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; | |||
3 | use assists::utils::test_related_attribute; | 3 | use assists::utils::test_related_attribute; |
4 | use cfg::CfgExpr; | 4 | use cfg::CfgExpr; |
5 | use hir::{AsAssocItem, HasAttrs, HasSource, Semantics}; | 5 | use hir::{AsAssocItem, HasAttrs, HasSource, Semantics}; |
6 | use ide_db::RootDatabase; | 6 | use ide_db::{defs::Definition, RootDatabase}; |
7 | use itertools::Itertools; | 7 | use itertools::Itertools; |
8 | use syntax::{ | 8 | use syntax::{ |
9 | ast::{self, AstNode, AttrsOwner, ModuleItemOwner}, | 9 | ast::{self, AstNode, AttrsOwner, ModuleItemOwner}, |
@@ -96,21 +96,26 @@ impl Runnable { | |||
96 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { | 96 | pub(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() | |
102 | pub(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 | ||
116 | pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> { | 121 | pub(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 | ||
148 | fn runnable_doctest(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { | 153 | pub(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. | ||
177 | pub(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 | ||
164 | fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Option<Runnable> { | 198 | fn 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 | ||
256 | fn 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 |
278 | fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool { | 292 | fn 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 |
333 | fn main() {} | 347 | fn 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 |
429 | fn main() {} | 443 | fn 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 |
577 | fn main() {} | 591 | fn main() {} |
578 | 592 | ||
579 | struct Data; | 593 | struct Data; |
@@ -625,7 +639,7 @@ impl Data { | |||
625 | check( | 639 | check( |
626 | r#" | 640 | r#" |
627 | //- /lib.rs | 641 | //- /lib.rs |
628 | <|> | 642 | $0 |
629 | mod test_mod { | 643 | mod 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 |
683 | mod root_tests { | 697 | mod 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")] |
825 | fn test_foo1() {} | 839 | fn 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"))] |
870 | fn test_foo1() {} | 884 | fn 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 |
923 | mod test_mod { | 937 | mod test_mod { |
924 | fn foo1() {} | 938 | fn foo1() {} |
925 | } | 939 | } |
@@ -938,7 +952,7 @@ mod test_mod { | |||
938 | //- /lib.rs | 952 | //- /lib.rs |
939 | mod foo; | 953 | mod foo; |
940 | //- /foo.rs | 954 | //- /foo.rs |
941 | struct Foo;<|> | 955 | struct Foo;$0 |
942 | impl Foo { | 956 | impl Foo { |
943 | /// ``` | 957 | /// ``` |
944 | /// let x = 5; | 958 | /// let x = 5; |