diff options
Diffstat (limited to 'crates/ide/src/runnables.rs')
-rw-r--r-- | crates/ide/src/runnables.rs | 110 |
1 files changed, 62 insertions, 48 deletions
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 557563d7e..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 { |