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.rs366
1 files changed, 125 insertions, 241 deletions
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 96462a7b0..2f2b99130 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, SymbolKind,
16};
14 17
15#[derive(Debug, Clone)] 18#[derive(Debug, Clone)]
16pub struct Runnable { 19pub struct Runnable {
@@ -101,124 +104,113 @@ 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::Impl(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(
174 141 sema.db,
175 let nav = if let RunnableKind::DocTest { .. } = kind { 142 InFile::new(file_id.into(), &func),
176 NavigationTarget::from_doc_commented( 143 SymbolKind::Function,
177 sema.db, 144 );
178 InFile::new(file_id.into(), &fn_def), 145 let cfg = def.attrs(sema.db).cfg();
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 }) 146 Some(Runnable { nav, kind, cfg })
185} 147}
186 148
187fn runnable_struct( 149fn runnable_doctest(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> {
188 sema: &Semantics<RootDatabase>, 150 match_ast! {
189 struct_def: ast::Struct, 151 match item {
190 file_id: FileId, 152 ast::Fn(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
191) -> Option<Runnable> { 153 ast::Struct(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
192 let name_string = struct_def.name()?.text().to_string(); 154 ast::Enum(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
155 ast::Union(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
156 ast::Trait(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
157 ast::Const(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
158 ast::Static(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
159 ast::TypeAlias(it) => module_def_doctest(sema, sema.to_def(&it)?.into()),
160 _ => None,
161 }
162 }
163}
193 164
194 let attrs = 165fn 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)); 166 let attrs = match def {
167 hir::ModuleDef::Module(it) => it.attrs(sema.db),
168 hir::ModuleDef::Function(it) => it.attrs(sema.db),
169 hir::ModuleDef::Adt(it) => it.attrs(sema.db),
170 hir::ModuleDef::Variant(it) => it.attrs(sema.db),
171 hir::ModuleDef::Const(it) => it.attrs(sema.db),
172 hir::ModuleDef::Static(it) => it.attrs(sema.db),
173 hir::ModuleDef::Trait(it) => it.attrs(sema.db),
174 hir::ModuleDef::TypeAlias(it) => it.attrs(sema.db),
175 hir::ModuleDef::BuiltinType(_) => return None,
176 };
196 if !has_runnable_doc_test(&attrs) { 177 if !has_runnable_doc_test(&attrs) {
197 return None; 178 return None;
198 } 179 }
199 let cfg = attrs.cfg(); 180 let def_name = def.name(sema.db).map(|it| it.to_string());
200 181 let test_id = def
201 let test_id = match sema.to_def(&struct_def).map(|def| def.module(sema.db)) { 182 .canonical_path(sema.db)
202 Some(module) => { 183 // This probably belongs to canonical path?
203 let path_iter = module 184 .map(|path| {
204 .path_to_root(sema.db) 185 let assoc_def = match def {
205 .into_iter() 186 hir::ModuleDef::Function(it) => it.as_assoc_item(sema.db),
206 .rev() 187 hir::ModuleDef::Const(it) => it.as_assoc_item(sema.db),
207 .filter_map(|it| it.name(sema.db)) 188 hir::ModuleDef::TypeAlias(it) => it.as_assoc_item(sema.db),
208 .map(|name| name.to_string()); 189 _ => None,
209 let path = path_iter.chain(std::iter::once(name_string)).join("::"); 190 };
210 191 // FIXME: this also looks very wrong
211 TestId::Path(path) 192 if let Some(assoc_def) = assoc_def {
212 } 193 if let hir::AssocItemContainer::Impl(imp) = assoc_def.container(sema.db) {
213 None => TestId::Name(name_string), 194 if let Some(adt) = imp.target_ty(sema.db).as_adt() {
214 }; 195 let name = adt.name(sema.db).to_string();
215 196 let idx = path.rfind(':').unwrap_or(0);
216 let nav = NavigationTarget::from_doc_commented( 197 let (prefix, suffix) = path.split_at(idx);
217 sema.db, 198 return format!("{}{}::{}", prefix, name, suffix);
218 InFile::new(file_id.into(), &struct_def), 199 }
219 InFile::new(file_id.into(), &struct_def), 200 }
220 ); 201 }
221 Some(Runnable { nav, kind: RunnableKind::DocTest { test_id }, cfg }) 202 path
203 })
204 .map(TestId::Path)
205 .or_else(|| def_name.clone().map(TestId::Name))?;
206
207 let mut nav = def.try_to_nav(sema.db)?;
208 nav.focus_range = None;
209 nav.description = None;
210 nav.docs = None;
211 nav.kind = None;
212 let res = Runnable { nav, kind: RunnableKind::DocTest { test_id }, cfg: attrs.cfg() };
213 Some(res)
222} 214}
223 215
224#[derive(Debug, Copy, Clone)] 216#[derive(Debug, Copy, Clone)]
@@ -262,11 +254,7 @@ fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
262 }) 254 })
263} 255}
264 256
265fn runnable_mod( 257fn 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) { 258 if !has_test_function_or_multiple_test_submodules(&module) {
271 return None; 259 return None;
272 } 260 }
@@ -279,7 +267,8 @@ fn runnable_mod(
279 .filter_map(|it| it.name(sema.db)) 267 .filter_map(|it| it.name(sema.db))
280 .join("::"); 268 .join("::");
281 269
282 let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module)); 270 let def = sema.to_def(&module)?;
271 let attrs = def.attrs(sema.db);
283 let cfg = attrs.cfg(); 272 let cfg = attrs.cfg();
284 let nav = module_def.to_nav(sema.db); 273 let nav = module_def.to_nav(sema.db);
285 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg }) 274 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg })
@@ -319,7 +308,7 @@ mod tests {
319 308
320 use crate::fixture; 309 use crate::fixture;
321 310
322 use super::{RunnableAction, BENCH, BIN, DOCTEST, TEST}; 311 use super::*;
323 312
324 fn check( 313 fn check(
325 ra_fixture: &str, 314 ra_fixture: &str,
@@ -363,14 +352,9 @@ fn bench() {}
363 0, 352 0,
364 ), 353 ),
365 full_range: 1..13, 354 full_range: 1..13,
366 focus_range: Some( 355 focus_range: 4..8,
367 4..8,
368 ),
369 name: "main", 356 name: "main",
370 kind: FN, 357 kind: Function,
371 container_name: None,
372 description: None,
373 docs: None,
374 }, 358 },
375 kind: Bin, 359 kind: Bin,
376 cfg: None, 360 cfg: None,
@@ -381,14 +365,9 @@ fn bench() {}
381 0, 365 0,
382 ), 366 ),
383 full_range: 15..39, 367 full_range: 15..39,
384 focus_range: Some( 368 focus_range: 26..34,
385 26..34,
386 ),
387 name: "test_foo", 369 name: "test_foo",
388 kind: FN, 370 kind: Function,
389 container_name: None,
390 description: None,
391 docs: None,
392 }, 371 },
393 kind: Test { 372 kind: Test {
394 test_id: Path( 373 test_id: Path(
@@ -406,14 +385,9 @@ fn bench() {}
406 0, 385 0,
407 ), 386 ),
408 full_range: 41..75, 387 full_range: 41..75,
409 focus_range: Some( 388 focus_range: 62..70,
410 62..70,
411 ),
412 name: "test_foo", 389 name: "test_foo",
413 kind: FN, 390 kind: Function,
414 container_name: None,
415 description: None,
416 docs: None,
417 }, 391 },
418 kind: Test { 392 kind: Test {
419 test_id: Path( 393 test_id: Path(
@@ -431,14 +405,9 @@ fn bench() {}
431 0, 405 0,
432 ), 406 ),
433 full_range: 77..99, 407 full_range: 77..99,
434 focus_range: Some( 408 focus_range: 89..94,
435 89..94,
436 ),
437 name: "bench", 409 name: "bench",
438 kind: FN, 410 kind: Function,
439 container_name: None,
440 description: None,
441 docs: None,
442 }, 411 },
443 kind: Bench { 412 kind: Bench {
444 test_id: Path( 413 test_id: Path(
@@ -528,14 +497,9 @@ struct StructWithRunnable(String);
528 0, 497 0,
529 ), 498 ),
530 full_range: 1..13, 499 full_range: 1..13,
531 focus_range: Some( 500 focus_range: 4..8,
532 4..8,
533 ),
534 name: "main", 501 name: "main",
535 kind: FN, 502 kind: Function,
536 container_name: None,
537 description: None,
538 docs: None,
539 }, 503 },
540 kind: Bin, 504 kind: Bin,
541 cfg: None, 505 cfg: None,
@@ -546,12 +510,7 @@ struct StructWithRunnable(String);
546 0, 510 0,
547 ), 511 ),
548 full_range: 15..74, 512 full_range: 15..74,
549 focus_range: None,
550 name: "should_have_runnable", 513 name: "should_have_runnable",
551 kind: FN,
552 container_name: None,
553 description: None,
554 docs: None,
555 }, 514 },
556 kind: DocTest { 515 kind: DocTest {
557 test_id: Path( 516 test_id: Path(
@@ -566,12 +525,7 @@ struct StructWithRunnable(String);
566 0, 525 0,
567 ), 526 ),
568 full_range: 76..148, 527 full_range: 76..148,
569 focus_range: None,
570 name: "should_have_runnable_1", 528 name: "should_have_runnable_1",
571 kind: FN,
572 container_name: None,
573 description: None,
574 docs: None,
575 }, 529 },
576 kind: DocTest { 530 kind: DocTest {
577 test_id: Path( 531 test_id: Path(
@@ -586,12 +540,7 @@ struct StructWithRunnable(String);
586 0, 540 0,
587 ), 541 ),
588 full_range: 150..254, 542 full_range: 150..254,
589 focus_range: None,
590 name: "should_have_runnable_2", 543 name: "should_have_runnable_2",
591 kind: FN,
592 container_name: None,
593 description: None,
594 docs: None,
595 }, 544 },
596 kind: DocTest { 545 kind: DocTest {
597 test_id: Path( 546 test_id: Path(
@@ -606,12 +555,7 @@ struct StructWithRunnable(String);
606 0, 555 0,
607 ), 556 ),
608 full_range: 756..821, 557 full_range: 756..821,
609 focus_range: None,
610 name: "StructWithRunnable", 558 name: "StructWithRunnable",
611 kind: STRUCT,
612 container_name: None,
613 description: None,
614 docs: None,
615 }, 559 },
616 kind: DocTest { 560 kind: DocTest {
617 test_id: Path( 561 test_id: Path(
@@ -650,14 +594,9 @@ impl Data {
650 0, 594 0,
651 ), 595 ),
652 full_range: 1..13, 596 full_range: 1..13,
653 focus_range: Some( 597 focus_range: 4..8,
654 4..8,
655 ),
656 name: "main", 598 name: "main",
657 kind: FN, 599 kind: Function,
658 container_name: None,
659 description: None,
660 docs: None,
661 }, 600 },
662 kind: Bin, 601 kind: Bin,
663 cfg: None, 602 cfg: None,
@@ -668,12 +607,7 @@ impl Data {
668 0, 607 0,
669 ), 608 ),
670 full_range: 44..98, 609 full_range: 44..98,
671 focus_range: None,
672 name: "foo", 610 name: "foo",
673 kind: FN,
674 container_name: None,
675 description: None,
676 docs: None,
677 }, 611 },
678 kind: DocTest { 612 kind: DocTest {
679 test_id: Path( 613 test_id: Path(
@@ -707,14 +641,9 @@ mod test_mod {
707 0, 641 0,
708 ), 642 ),
709 full_range: 1..51, 643 full_range: 1..51,
710 focus_range: Some( 644 focus_range: 5..13,
711 5..13,
712 ),
713 name: "test_mod", 645 name: "test_mod",
714 kind: MODULE, 646 kind: Module,
715 container_name: None,
716 description: None,
717 docs: None,
718 }, 647 },
719 kind: TestMod { 648 kind: TestMod {
720 path: "test_mod", 649 path: "test_mod",
@@ -727,14 +656,9 @@ mod test_mod {
727 0, 656 0,
728 ), 657 ),
729 full_range: 20..49, 658 full_range: 20..49,
730 focus_range: Some( 659 focus_range: 35..44,
731 35..44,
732 ),
733 name: "test_foo1", 660 name: "test_foo1",
734 kind: FN, 661 kind: Function,
735 container_name: None,
736 description: None,
737 docs: None,
738 }, 662 },
739 kind: Test { 663 kind: Test {
740 test_id: Path( 664 test_id: Path(
@@ -787,14 +711,9 @@ mod root_tests {
787 0, 711 0,
788 ), 712 ),
789 full_range: 22..323, 713 full_range: 22..323,
790 focus_range: Some( 714 focus_range: 26..40,
791 26..40,
792 ),
793 name: "nested_tests_0", 715 name: "nested_tests_0",
794 kind: MODULE, 716 kind: Module,
795 container_name: None,
796 description: None,
797 docs: None,
798 }, 717 },
799 kind: TestMod { 718 kind: TestMod {
800 path: "root_tests::nested_tests_0", 719 path: "root_tests::nested_tests_0",
@@ -807,14 +726,9 @@ mod root_tests {
807 0, 726 0,
808 ), 727 ),
809 full_range: 51..192, 728 full_range: 51..192,
810 focus_range: Some( 729 focus_range: 55..69,
811 55..69,
812 ),
813 name: "nested_tests_1", 730 name: "nested_tests_1",
814 kind: MODULE, 731 kind: Module,
815 container_name: None,
816 description: None,
817 docs: None,
818 }, 732 },
819 kind: TestMod { 733 kind: TestMod {
820 path: "root_tests::nested_tests_0::nested_tests_1", 734 path: "root_tests::nested_tests_0::nested_tests_1",
@@ -827,14 +741,9 @@ mod root_tests {
827 0, 741 0,
828 ), 742 ),
829 full_range: 84..126, 743 full_range: 84..126,
830 focus_range: Some( 744 focus_range: 107..121,
831 107..121,
832 ),
833 name: "nested_test_11", 745 name: "nested_test_11",
834 kind: FN, 746 kind: Function,
835 container_name: None,
836 description: None,
837 docs: None,
838 }, 747 },
839 kind: Test { 748 kind: Test {
840 test_id: Path( 749 test_id: Path(
@@ -852,14 +761,9 @@ mod root_tests {
852 0, 761 0,
853 ), 762 ),
854 full_range: 140..182, 763 full_range: 140..182,
855 focus_range: Some( 764 focus_range: 163..177,
856 163..177,
857 ),
858 name: "nested_test_12", 765 name: "nested_test_12",
859 kind: FN, 766 kind: Function,
860 container_name: None,
861 description: None,
862 docs: None,
863 }, 767 },
864 kind: Test { 768 kind: Test {
865 test_id: Path( 769 test_id: Path(
@@ -877,14 +781,9 @@ mod root_tests {
877 0, 781 0,
878 ), 782 ),
879 full_range: 202..286, 783 full_range: 202..286,
880 focus_range: Some( 784 focus_range: 206..220,
881 206..220,
882 ),
883 name: "nested_tests_2", 785 name: "nested_tests_2",
884 kind: MODULE, 786 kind: Module,
885 container_name: None,
886 description: None,
887 docs: None,
888 }, 787 },
889 kind: TestMod { 788 kind: TestMod {
890 path: "root_tests::nested_tests_0::nested_tests_2", 789 path: "root_tests::nested_tests_0::nested_tests_2",
@@ -897,14 +796,9 @@ mod root_tests {
897 0, 796 0,
898 ), 797 ),
899 full_range: 235..276, 798 full_range: 235..276,
900 focus_range: Some( 799 focus_range: 258..271,
901 258..271,
902 ),
903 name: "nested_test_2", 800 name: "nested_test_2",
904 kind: FN, 801 kind: Function,
905 container_name: None,
906 description: None,
907 docs: None,
908 }, 802 },
909 kind: Test { 803 kind: Test {
910 test_id: Path( 804 test_id: Path(
@@ -940,14 +834,9 @@ fn test_foo1() {}
940 0, 834 0,
941 ), 835 ),
942 full_range: 1..50, 836 full_range: 1..50,
943 focus_range: Some( 837 focus_range: 36..45,
944 36..45,
945 ),
946 name: "test_foo1", 838 name: "test_foo1",
947 kind: FN, 839 kind: Function,
948 container_name: None,
949 description: None,
950 docs: None,
951 }, 840 },
952 kind: Test { 841 kind: Test {
953 test_id: Path( 842 test_id: Path(
@@ -990,14 +879,9 @@ fn test_foo1() {}
990 0, 879 0,
991 ), 880 ),
992 full_range: 1..72, 881 full_range: 1..72,
993 focus_range: Some( 882 focus_range: 58..67,
994 58..67,
995 ),
996 name: "test_foo1", 883 name: "test_foo1",
997 kind: FN, 884 kind: Function,
998 container_name: None,
999 description: None,
1000 docs: None,
1001 }, 885 },
1002 kind: Test { 886 kind: Test {
1003 test_id: Path( 887 test_id: Path(