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.rs203
1 files changed, 94 insertions, 109 deletions
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 646f63704..2f465c195 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -2,7 +2,7 @@ use std::fmt;
2 2
3use assists::utils::test_related_attribute; 3use assists::utils::test_related_attribute;
4use cfg::CfgExpr; 4use cfg::CfgExpr;
5use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; 5use hir::{AsAssocItem, HasAttrs, InFile, Semantics};
6use ide_db::RootDatabase; 6use ide_db::RootDatabase;
7use itertools::Itertools; 7use itertools::Itertools;
8use syntax::{ 8use syntax::{
@@ -10,7 +10,10 @@ use syntax::{
10 match_ast, SyntaxNode, 10 match_ast, SyntaxNode,
11}; 11};
12 12
13use crate::{display::ToNav, FileId, NavigationTarget}; 13use crate::{
14 display::{ToNav, TryToNav},
15 FileId, NavigationTarget,
16};
14 17
15#[derive(Debug, Clone)] 18#[derive(Debug, Clone)]
16pub struct Runnable { 19pub struct Runnable {
@@ -101,124 +104,109 @@ pub(crate) fn runnable(
101 item: SyntaxNode, 104 item: SyntaxNode,
102 file_id: FileId, 105 file_id: FileId,
103) -> Option<Runnable> { 106) -> Option<Runnable> {
104 match_ast! { 107 let runnable_item = match_ast! {
105 match item { 108 match (item.clone()) {
106 ast::Struct(it) => runnable_struct(sema, it, file_id),
107 ast::Fn(it) => runnable_fn(sema, it, file_id), 109 ast::Fn(it) => runnable_fn(sema, it, file_id),
108 ast::Module(it) => runnable_mod(sema, it, file_id), 110 ast::Module(it) => runnable_mod(sema, it),
109 _ => None, 111 _ => None,
110 } 112 }
111 } 113 };
114 runnable_item.or_else(|| runnable_doctest(sema, item))
112} 115}
113 116
114fn runnable_fn( 117fn runnable_fn(sema: &Semantics<RootDatabase>, func: ast::Fn, file_id: FileId) -> Option<Runnable> {
115 sema: &Semantics<RootDatabase>, 118 let def = sema.to_def(&func)?;
116 fn_def: ast::Fn, 119 let name_string = func.name()?.text().to_string();
117 file_id: FileId,
118) -> Option<Runnable> {
119 let name_string = fn_def.name()?.text().to_string();
120 120
121 let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def));
122 let kind = if name_string == "main" { 121 let kind = if name_string == "main" {
123 RunnableKind::Bin 122 RunnableKind::Bin
124 } else { 123 } else {
125 let test_id = match sema.to_def(&fn_def).map(|def| def.module(sema.db)) { 124 let canonical_path = sema.to_def(&func).and_then(|def| {
126 Some(module) => { 125 let def: hir::ModuleDef = def.into();
127 let def = sema.to_def(&fn_def)?; 126 def.canonical_path(sema.db)
128 let impl_trait_name = def.as_assoc_item(sema.db).and_then(|assoc_item| { 127 });
129 match assoc_item.container(sema.db) { 128 let test_id = canonical_path.map(TestId::Path).unwrap_or(TestId::Name(name_string));
130 hir::AssocItemContainer::Trait(trait_item) => { 129
131 Some(trait_item.name(sema.db).to_string()) 130 if test_related_attribute(&func).is_some() {
132 } 131 let attr = TestAttr::from_fn(&func);
133 hir::AssocItemContainer::ImplDef(impl_def) => impl_def
134 .target_ty(sema.db)
135 .as_adt()
136 .map(|adt| adt.name(sema.db).to_string()),
137 }
138 });
139
140 let path_iter = module
141 .path_to_root(sema.db)
142 .into_iter()
143 .rev()
144 .filter_map(|it| it.name(sema.db))
145 .map(|name| name.to_string());
146
147 let path = if let Some(impl_trait_name) = impl_trait_name {
148 path_iter
149 .chain(std::iter::once(impl_trait_name))
150 .chain(std::iter::once(name_string))
151 .join("::")
152 } else {
153 path_iter.chain(std::iter::once(name_string)).join("::")
154 };
155
156 TestId::Path(path)
157 }
158 None => TestId::Name(name_string),
159 };
160
161 if test_related_attribute(&fn_def).is_some() {
162 let attr = TestAttr::from_fn(&fn_def);
163 RunnableKind::Test { test_id, attr } 132 RunnableKind::Test { test_id, attr }
164 } else if fn_def.has_atom_attr("bench") { 133 } else if func.has_atom_attr("bench") {
165 RunnableKind::Bench { test_id } 134 RunnableKind::Bench { test_id }
166 } else if has_runnable_doc_test(&attrs) {
167 RunnableKind::DocTest { test_id }
168 } else { 135 } else {
169 return None; 136 return None;
170 } 137 }
171 }; 138 };
172 139
173 let cfg = attrs.cfg(); 140 let nav = NavigationTarget::from_named(sema.db, InFile::new(file_id.into(), &func));
174 141 let cfg = def.attrs(sema.db).cfg();
175 let nav = if let RunnableKind::DocTest { .. } = kind {
176 NavigationTarget::from_doc_commented(
177 sema.db,
178 InFile::new(file_id.into(), &fn_def),
179 InFile::new(file_id.into(), &fn_def),
180 )
181 } else {
182 NavigationTarget::from_named(sema.db, InFile::new(file_id.into(), &fn_def))
183 };
184 Some(Runnable { nav, kind, cfg }) 142 Some(Runnable { nav, kind, cfg })
185} 143}
186 144
187fn runnable_struct( 145fn runnable_doctest(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> {
188 sema: &Semantics<RootDatabase>, 146 match_ast! {
189 struct_def: ast::Struct, 147 match item {
190 file_id: FileId, 148 ast::Fn(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
191) -> Option<Runnable> { 149 ast::Struct(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
192 let name_string = struct_def.name()?.text().to_string(); 150 ast::Enum(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
151 ast::Union(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
152 ast::Trait(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
153 ast::Const(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
154 ast::Static(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
155 ast::TypeAlias(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
156 _ => None,
157 }
158 }
159}
193 160
194 let attrs = 161fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Option<Runnable> {
195 Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &struct_def)); 162 let attrs = match def {
163 hir::ModuleDef::Module(it) => it.attrs(sema.db),
164 hir::ModuleDef::Function(it) => it.attrs(sema.db),
165 hir::ModuleDef::Adt(it) => it.attrs(sema.db),
166 hir::ModuleDef::EnumVariant(it) => it.attrs(sema.db),
167 hir::ModuleDef::Const(it) => it.attrs(sema.db),
168 hir::ModuleDef::Static(it) => it.attrs(sema.db),
169 hir::ModuleDef::Trait(it) => it.attrs(sema.db),
170 hir::ModuleDef::TypeAlias(it) => it.attrs(sema.db),
171 hir::ModuleDef::BuiltinType(_) => return None,
172 };
196 if !has_runnable_doc_test(&attrs) { 173 if !has_runnable_doc_test(&attrs) {
197 return None; 174 return None;
198 } 175 }
199 let cfg = attrs.cfg(); 176 let def_name = def.name(sema.db).map(|it| it.to_string());
200 177 let test_id = def
201 let test_id = match sema.to_def(&struct_def).map(|def| def.module(sema.db)) { 178 .canonical_path(sema.db)
202 Some(module) => { 179 // This probably belongs to canonical path?
203 let path_iter = module 180 .map(|path| {
204 .path_to_root(sema.db) 181 let assoc_def = match def {
205 .into_iter() 182 hir::ModuleDef::Function(it) => it.as_assoc_item(sema.db),
206 .rev() 183 hir::ModuleDef::Const(it) => it.as_assoc_item(sema.db),
207 .filter_map(|it| it.name(sema.db)) 184 hir::ModuleDef::TypeAlias(it) => it.as_assoc_item(sema.db),
208 .map(|name| name.to_string()); 185 _ => None,
209 let path = path_iter.chain(std::iter::once(name_string)).join("::"); 186 };
210 187 // FIXME: this also looks very wrong
211 TestId::Path(path) 188 if let Some(assoc_def) = assoc_def {
212 } 189 if let hir::AssocItemContainer::Impl(imp) = assoc_def.container(sema.db) {
213 None => TestId::Name(name_string), 190 if let Some(adt) = imp.target_ty(sema.db).as_adt() {
214 }; 191 let name = adt.name(sema.db).to_string();
215 192 let idx = path.rfind(':').unwrap_or(0);
216 let nav = NavigationTarget::from_doc_commented( 193 let (prefix, suffix) = path.split_at(idx);
217 sema.db, 194 return format!("{}{}::{}", prefix, name, suffix);
218 InFile::new(file_id.into(), &struct_def), 195 }
219 InFile::new(file_id.into(), &struct_def), 196 }
220 ); 197 }
221 Some(Runnable { nav, kind: RunnableKind::DocTest { test_id }, cfg }) 198 path
199 })
200 .map(TestId::Path)
201 .or_else(|| def_name.clone().map(TestId::Name))?;
202
203 let mut nav = def.try_to_nav(sema.db)?;
204 nav.focus_range = None;
205 nav.description = None;
206 nav.docs = None;
207 nav.kind = syntax::SyntaxKind::COMMENT;
208 let res = Runnable { nav, kind: RunnableKind::DocTest { test_id }, cfg: attrs.cfg() };
209 Some(res)
222} 210}
223 211
224#[derive(Debug, Copy, Clone)] 212#[derive(Debug, Copy, Clone)]
@@ -262,11 +250,7 @@ fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
262 }) 250 })
263} 251}
264 252
265fn runnable_mod( 253fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> {
266 sema: &Semantics<RootDatabase>,
267 module: ast::Module,
268 file_id: FileId,
269) -> Option<Runnable> {
270 if !has_test_function_or_multiple_test_submodules(&module) { 254 if !has_test_function_or_multiple_test_submodules(&module) {
271 return None; 255 return None;
272 } 256 }
@@ -279,7 +263,8 @@ fn runnable_mod(
279 .filter_map(|it| it.name(sema.db)) 263 .filter_map(|it| it.name(sema.db))
280 .join("::"); 264 .join("::");
281 265
282 let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module)); 266 let def = sema.to_def(&module)?;
267 let attrs = def.attrs(sema.db);
283 let cfg = attrs.cfg(); 268 let cfg = attrs.cfg();
284 let nav = module_def.to_nav(sema.db); 269 let nav = module_def.to_nav(sema.db);
285 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg }) 270 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg })
@@ -319,7 +304,7 @@ mod tests {
319 304
320 use crate::fixture; 305 use crate::fixture;
321 306
322 use super::{RunnableAction, BENCH, BIN, DOCTEST, TEST}; 307 use super::*;
323 308
324 fn check( 309 fn check(
325 ra_fixture: &str, 310 ra_fixture: &str,
@@ -548,7 +533,7 @@ struct StructWithRunnable(String);
548 full_range: 15..74, 533 full_range: 15..74,
549 focus_range: None, 534 focus_range: None,
550 name: "should_have_runnable", 535 name: "should_have_runnable",
551 kind: FN, 536 kind: COMMENT,
552 container_name: None, 537 container_name: None,
553 description: None, 538 description: None,
554 docs: None, 539 docs: None,
@@ -568,7 +553,7 @@ struct StructWithRunnable(String);
568 full_range: 76..148, 553 full_range: 76..148,
569 focus_range: None, 554 focus_range: None,
570 name: "should_have_runnable_1", 555 name: "should_have_runnable_1",
571 kind: FN, 556 kind: COMMENT,
572 container_name: None, 557 container_name: None,
573 description: None, 558 description: None,
574 docs: None, 559 docs: None,
@@ -588,7 +573,7 @@ struct StructWithRunnable(String);
588 full_range: 150..254, 573 full_range: 150..254,
589 focus_range: None, 574 focus_range: None,
590 name: "should_have_runnable_2", 575 name: "should_have_runnable_2",
591 kind: FN, 576 kind: COMMENT,
592 container_name: None, 577 container_name: None,
593 description: None, 578 description: None,
594 docs: None, 579 docs: None,
@@ -608,7 +593,7 @@ struct StructWithRunnable(String);
608 full_range: 756..821, 593 full_range: 756..821,
609 focus_range: None, 594 focus_range: None,
610 name: "StructWithRunnable", 595 name: "StructWithRunnable",
611 kind: STRUCT, 596 kind: COMMENT,
612 container_name: None, 597 container_name: None,
613 description: None, 598 description: None,
614 docs: None, 599 docs: None,
@@ -670,7 +655,7 @@ impl Data {
670 full_range: 44..98, 655 full_range: 44..98,
671 focus_range: None, 656 focus_range: None,
672 name: "foo", 657 name: "foo",
673 kind: FN, 658 kind: COMMENT,
674 container_name: None, 659 container_name: None,
675 description: None, 660 description: None,
676 docs: None, 661 docs: None,