aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ide/src/display/navigation_target.rs19
-rw-r--r--crates/ide/src/runnables.rs191
2 files changed, 89 insertions, 121 deletions
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs
index 8410bf5a2..522607cb7 100644
--- a/crates/ide/src/display/navigation_target.rs
+++ b/crates/ide/src/display/navigation_target.rs
@@ -117,25 +117,6 @@ impl NavigationTarget {
117 ) 117 )
118 } 118 }
119 119
120 /// Allows `NavigationTarget` to be created from a `DocCommentsOwner` and a `NameOwner`
121 pub(crate) fn from_doc_commented(
122 db: &RootDatabase,
123 named: InFile<&dyn ast::NameOwner>,
124 node: InFile<&dyn ast::DocCommentsOwner>,
125 ) -> NavigationTarget {
126 let name =
127 named.value.name().map(|it| it.text().clone()).unwrap_or_else(|| SmolStr::new("_"));
128 let frange = node.map(|it| it.syntax()).original_file_range(db);
129
130 NavigationTarget::from_syntax(
131 frange.file_id,
132 name,
133 None,
134 frange.range,
135 node.value.syntax().kind(),
136 )
137 }
138
139 fn from_syntax( 120 fn from_syntax(
140 file_id: FileId, 121 file_id: FileId,
141 name: SmolStr, 122 name: SmolStr,
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index a2a0ad43d..2f465c195 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -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,125 +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), 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 def = sema.to_def(&fn_def)?;
120 let name_string = fn_def.name()?.text().to_string();
121 120
122 let attrs = def.attrs(sema.db);
123 let kind = if name_string == "main" { 121 let kind = if name_string == "main" {
124 RunnableKind::Bin 122 RunnableKind::Bin
125 } else { 123 } else {
126 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| {
127 Some(module) => { 125 let def: hir::ModuleDef = def.into();
128 let def = sema.to_def(&fn_def)?; 126 def.canonical_path(sema.db)
129 let impl_trait_name = def.as_assoc_item(sema.db).and_then(|assoc_item| { 127 });
130 match assoc_item.container(sema.db) { 128 let test_id = canonical_path.map(TestId::Path).unwrap_or(TestId::Name(name_string));
131 hir::AssocItemContainer::Trait(trait_item) => { 129
132 Some(trait_item.name(sema.db).to_string()) 130 if test_related_attribute(&func).is_some() {
133 } 131 let attr = TestAttr::from_fn(&func);
134 hir::AssocItemContainer::Impl(impl_def) => impl_def
135 .target_ty(sema.db)
136 .as_adt()
137 .map(|adt| adt.name(sema.db).to_string()),
138 }
139 });
140
141 let path_iter = module
142 .path_to_root(sema.db)
143 .into_iter()
144 .rev()
145 .filter_map(|it| it.name(sema.db))
146 .map(|name| name.to_string());
147
148 let path = if let Some(impl_trait_name) = impl_trait_name {
149 path_iter
150 .chain(std::iter::once(impl_trait_name))
151 .chain(std::iter::once(name_string))
152 .join("::")
153 } else {
154 path_iter.chain(std::iter::once(name_string)).join("::")
155 };
156
157 TestId::Path(path)
158 }
159 None => TestId::Name(name_string),
160 };
161
162 if test_related_attribute(&fn_def).is_some() {
163 let attr = TestAttr::from_fn(&fn_def);
164 RunnableKind::Test { test_id, attr } 132 RunnableKind::Test { test_id, attr }
165 } else if fn_def.has_atom_attr("bench") { 133 } else if func.has_atom_attr("bench") {
166 RunnableKind::Bench { test_id } 134 RunnableKind::Bench { test_id }
167 } else if has_runnable_doc_test(&attrs) {
168 RunnableKind::DocTest { test_id }
169 } else { 135 } else {
170 return None; 136 return None;
171 } 137 }
172 }; 138 };
173 139
174 let cfg = attrs.cfg(); 140 let nav = NavigationTarget::from_named(sema.db, InFile::new(file_id.into(), &func));
175 141 let cfg = def.attrs(sema.db).cfg();
176 let nav = if let RunnableKind::DocTest { .. } = kind {
177 NavigationTarget::from_doc_commented(
178 sema.db,
179 InFile::new(file_id.into(), &fn_def),
180 InFile::new(file_id.into(), &fn_def),
181 )
182 } else {
183 NavigationTarget::from_named(sema.db, InFile::new(file_id.into(), &fn_def))
184 };
185 Some(Runnable { nav, kind, cfg }) 142 Some(Runnable { nav, kind, cfg })
186} 143}
187 144
188fn runnable_struct( 145fn runnable_doctest(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> {
189 sema: &Semantics<RootDatabase>, 146 match_ast! {
190 struct_def: ast::Struct, 147 match item {
191 file_id: FileId, 148 ast::Fn(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
192) -> Option<Runnable> { 149 ast::Struct(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
193 let def = sema.to_def(&struct_def)?; 150 ast::Enum(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
194 let name_string = struct_def.name()?.text().to_string(); 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}
195 160
196 let attrs = def.attrs(sema.db); 161fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Option<Runnable> {
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 };
197 if !has_runnable_doc_test(&attrs) { 173 if !has_runnable_doc_test(&attrs) {
198 return None; 174 return None;
199 } 175 }
200 let cfg = attrs.cfg(); 176 let def_name = def.name(sema.db).map(|it| it.to_string());
201 177 let test_id = def
202 let test_id = match sema.to_def(&struct_def).map(|def| def.module(sema.db)) { 178 .canonical_path(sema.db)
203 Some(module) => { 179 // This probably belongs to canonical path?
204 let path_iter = module 180 .map(|path| {
205 .path_to_root(sema.db) 181 let assoc_def = match def {
206 .into_iter() 182 hir::ModuleDef::Function(it) => it.as_assoc_item(sema.db),
207 .rev() 183 hir::ModuleDef::Const(it) => it.as_assoc_item(sema.db),
208 .filter_map(|it| it.name(sema.db)) 184 hir::ModuleDef::TypeAlias(it) => it.as_assoc_item(sema.db),
209 .map(|name| name.to_string()); 185 _ => None,
210 let path = path_iter.chain(std::iter::once(name_string)).join("::"); 186 };
211 187 // FIXME: this also looks very wrong
212 TestId::Path(path) 188 if let Some(assoc_def) = assoc_def {
213 } 189 if let hir::AssocItemContainer::Impl(imp) = assoc_def.container(sema.db) {
214 None => TestId::Name(name_string), 190 if let Some(adt) = imp.target_ty(sema.db).as_adt() {
215 }; 191 let name = adt.name(sema.db).to_string();
216 192 let idx = path.rfind(':').unwrap_or(0);
217 let nav = NavigationTarget::from_doc_commented( 193 let (prefix, suffix) = path.split_at(idx);
218 sema.db, 194 return format!("{}{}::{}", prefix, name, suffix);
219 InFile::new(file_id.into(), &struct_def), 195 }
220 InFile::new(file_id.into(), &struct_def), 196 }
221 ); 197 }
222 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)
223} 210}
224 211
225#[derive(Debug, Copy, Clone)] 212#[derive(Debug, Copy, Clone)]
@@ -317,7 +304,7 @@ mod tests {
317 304
318 use crate::fixture; 305 use crate::fixture;
319 306
320 use super::{RunnableAction, BENCH, BIN, DOCTEST, TEST}; 307 use super::*;
321 308
322 fn check( 309 fn check(
323 ra_fixture: &str, 310 ra_fixture: &str,
@@ -546,7 +533,7 @@ struct StructWithRunnable(String);
546 full_range: 15..74, 533 full_range: 15..74,
547 focus_range: None, 534 focus_range: None,
548 name: "should_have_runnable", 535 name: "should_have_runnable",
549 kind: FN, 536 kind: COMMENT,
550 container_name: None, 537 container_name: None,
551 description: None, 538 description: None,
552 docs: None, 539 docs: None,
@@ -566,7 +553,7 @@ struct StructWithRunnable(String);
566 full_range: 76..148, 553 full_range: 76..148,
567 focus_range: None, 554 focus_range: None,
568 name: "should_have_runnable_1", 555 name: "should_have_runnable_1",
569 kind: FN, 556 kind: COMMENT,
570 container_name: None, 557 container_name: None,
571 description: None, 558 description: None,
572 docs: None, 559 docs: None,
@@ -586,7 +573,7 @@ struct StructWithRunnable(String);
586 full_range: 150..254, 573 full_range: 150..254,
587 focus_range: None, 574 focus_range: None,
588 name: "should_have_runnable_2", 575 name: "should_have_runnable_2",
589 kind: FN, 576 kind: COMMENT,
590 container_name: None, 577 container_name: None,
591 description: None, 578 description: None,
592 docs: None, 579 docs: None,
@@ -606,7 +593,7 @@ struct StructWithRunnable(String);
606 full_range: 756..821, 593 full_range: 756..821,
607 focus_range: None, 594 focus_range: None,
608 name: "StructWithRunnable", 595 name: "StructWithRunnable",
609 kind: STRUCT, 596 kind: COMMENT,
610 container_name: None, 597 container_name: None,
611 description: None, 598 description: None,
612 docs: None, 599 docs: None,
@@ -668,7 +655,7 @@ impl Data {
668 full_range: 44..98, 655 full_range: 44..98,
669 focus_range: None, 656 focus_range: None,
670 name: "foo", 657 name: "foo",
671 kind: FN, 658 kind: COMMENT,
672 container_name: None, 659 container_name: None,
673 description: None, 660 description: None,
674 docs: None, 661 docs: None,