diff options
Diffstat (limited to 'crates/ide/src')
-rw-r--r-- | crates/ide/src/lib.rs | 11 | ||||
-rw-r--r-- | crates/ide/src/references/rename.rs | 54 | ||||
-rw-r--r-- | crates/ide/src/runnables.rs | 399 |
3 files changed, 446 insertions, 18 deletions
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index d1a250d48..a8b169e87 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -87,7 +87,7 @@ pub use crate::{ | |||
87 | pub use hir::{Documentation, Semantics}; | 87 | pub use hir::{Documentation, Semantics}; |
88 | pub use ide_assists::{Assist, AssistConfig, AssistId, AssistKind}; | 88 | pub use ide_assists::{Assist, AssistConfig, AssistId, AssistKind}; |
89 | pub use ide_completion::{ | 89 | pub use ide_completion::{ |
90 | CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, | 90 | CompletionConfig, CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit, |
91 | InsertTextFormat, | 91 | InsertTextFormat, |
92 | }; | 92 | }; |
93 | pub use ide_db::{ | 93 | pub use ide_db::{ |
@@ -447,6 +447,15 @@ impl Analysis { | |||
447 | self.with_db(|db| runnables::runnables(db, file_id)) | 447 | self.with_db(|db| runnables::runnables(db, file_id)) |
448 | } | 448 | } |
449 | 449 | ||
450 | /// Returns the set of tests for the given file position. | ||
451 | pub fn related_tests( | ||
452 | &self, | ||
453 | position: FilePosition, | ||
454 | search_scope: Option<SearchScope>, | ||
455 | ) -> Cancelable<Vec<Runnable>> { | ||
456 | self.with_db(|db| runnables::related_tests(db, position, search_scope)) | ||
457 | } | ||
458 | |||
450 | /// Computes syntax highlighting for the given file | 459 | /// Computes syntax highlighting for the given file |
451 | pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HlRange>> { | 460 | pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HlRange>> { |
452 | self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false)) | 461 | self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false)) |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index bb68bcc78..1e378279d 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -21,7 +21,7 @@ use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceCh | |||
21 | 21 | ||
22 | type RenameResult<T> = Result<T, RenameError>; | 22 | type RenameResult<T> = Result<T, RenameError>; |
23 | #[derive(Debug)] | 23 | #[derive(Debug)] |
24 | pub struct RenameError(pub(crate) String); | 24 | pub struct RenameError(String); |
25 | 25 | ||
26 | impl fmt::Display for RenameError { | 26 | impl fmt::Display for RenameError { |
27 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 27 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
@@ -47,16 +47,15 @@ pub(crate) fn prepare_rename( | |||
47 | let sema = Semantics::new(db); | 47 | let sema = Semantics::new(db); |
48 | let source_file = sema.parse(position.file_id); | 48 | let source_file = sema.parse(position.file_id); |
49 | let syntax = source_file.syntax(); | 49 | let syntax = source_file.syntax(); |
50 | let range = match &sema | 50 | let name_like = sema |
51 | .find_node_at_offset_with_descend(&syntax, position.offset) | 51 | .find_node_at_offset_with_descend(&syntax, position.offset) |
52 | .ok_or_else(|| format_err!("No references found at position"))? | 52 | .ok_or_else(|| format_err!("No references found at position"))?; |
53 | { | 53 | let node = match &name_like { |
54 | ast::NameLike::Name(it) => it.syntax(), | 54 | ast::NameLike::Name(it) => it.syntax(), |
55 | ast::NameLike::NameRef(it) => it.syntax(), | 55 | ast::NameLike::NameRef(it) => it.syntax(), |
56 | ast::NameLike::Lifetime(it) => it.syntax(), | 56 | ast::NameLike::Lifetime(it) => it.syntax(), |
57 | } | 57 | }; |
58 | .text_range(); | 58 | Ok(RangeInfo::new(sema.original_range(node).range, ())) |
59 | Ok(RangeInfo::new(range, ())) | ||
60 | } | 59 | } |
61 | 60 | ||
62 | // Feature: Rename | 61 | // Feature: Rename |
@@ -546,6 +545,8 @@ mod tests { | |||
546 | 545 | ||
547 | use crate::{fixture, FileId}; | 546 | use crate::{fixture, FileId}; |
548 | 547 | ||
548 | use super::{RangeInfo, RenameError}; | ||
549 | |||
549 | fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 550 | fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
550 | let ra_fixture_after = &trim_indent(ra_fixture_after); | 551 | let ra_fixture_after = &trim_indent(ra_fixture_after); |
551 | let (analysis, position) = fixture::position(ra_fixture_before); | 552 | let (analysis, position) = fixture::position(ra_fixture_before); |
@@ -591,6 +592,45 @@ mod tests { | |||
591 | expect.assert_debug_eq(&source_change) | 592 | expect.assert_debug_eq(&source_change) |
592 | } | 593 | } |
593 | 594 | ||
595 | fn check_prepare(ra_fixture: &str, expect: Expect) { | ||
596 | let (analysis, position) = fixture::position(ra_fixture); | ||
597 | let result = analysis | ||
598 | .prepare_rename(position) | ||
599 | .unwrap_or_else(|err| panic!("PrepareRename was cancelled: {}", err)); | ||
600 | match result { | ||
601 | Ok(RangeInfo { range, info: () }) => { | ||
602 | let source = analysis.file_text(position.file_id).unwrap(); | ||
603 | expect.assert_eq(&format!("{:?}: {}", range, &source[range])) | ||
604 | } | ||
605 | Err(RenameError(err)) => expect.assert_eq(&err), | ||
606 | }; | ||
607 | } | ||
608 | |||
609 | #[test] | ||
610 | fn test_prepare_rename_namelikes() { | ||
611 | check_prepare(r"fn name$0<'lifetime>() {}", expect![[r#"3..7: name"#]]); | ||
612 | check_prepare(r"fn name<'lifetime$0>() {}", expect![[r#"8..17: 'lifetime"#]]); | ||
613 | check_prepare(r"fn name<'lifetime>() { name$0(); }", expect![[r#"23..27: name"#]]); | ||
614 | } | ||
615 | |||
616 | #[test] | ||
617 | fn test_prepare_rename_in_macro() { | ||
618 | check_prepare( | ||
619 | r"macro_rules! foo { | ||
620 | ($ident:ident) => { | ||
621 | pub struct $ident; | ||
622 | } | ||
623 | } | ||
624 | foo!(Foo$0);", | ||
625 | expect![[r#"83..86: Foo"#]], | ||
626 | ); | ||
627 | } | ||
628 | |||
629 | #[test] | ||
630 | fn test_prepare_rename_keyword() { | ||
631 | check_prepare(r"struct$0 Foo;", expect![[r#"No references found at position"#]]); | ||
632 | } | ||
633 | |||
594 | #[test] | 634 | #[test] |
595 | fn test_rename_to_underscore() { | 635 | fn test_rename_to_underscore() { |
596 | check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#); | 636 | check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#); |
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 280565563..27d35de5b 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -1,10 +1,17 @@ | |||
1 | use std::fmt; | 1 | use std::fmt; |
2 | 2 | ||
3 | use ast::NameOwner; | ||
3 | use cfg::CfgExpr; | 4 | use cfg::CfgExpr; |
4 | use hir::{AsAssocItem, HasAttrs, HasSource, Semantics}; | 5 | use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics}; |
5 | use ide_assists::utils::test_related_attribute; | 6 | use ide_assists::utils::test_related_attribute; |
6 | use ide_db::{defs::Definition, RootDatabase, SymbolKind}; | 7 | use ide_db::{ |
8 | base_db::{FilePosition, FileRange}, | ||
9 | defs::Definition, | ||
10 | search::SearchScope, | ||
11 | RootDatabase, SymbolKind, | ||
12 | }; | ||
7 | use itertools::Itertools; | 13 | use itertools::Itertools; |
14 | use rustc_hash::FxHashSet; | ||
8 | use syntax::{ | 15 | use syntax::{ |
9 | ast::{self, AstNode, AttrsOwner}, | 16 | ast::{self, AstNode, AttrsOwner}, |
10 | match_ast, SyntaxNode, | 17 | match_ast, SyntaxNode, |
@@ -12,17 +19,17 @@ use syntax::{ | |||
12 | 19 | ||
13 | use crate::{ | 20 | use crate::{ |
14 | display::{ToNav, TryToNav}, | 21 | display::{ToNav, TryToNav}, |
15 | FileId, NavigationTarget, | 22 | references, FileId, NavigationTarget, |
16 | }; | 23 | }; |
17 | 24 | ||
18 | #[derive(Debug, Clone)] | 25 | #[derive(Debug, Clone, Hash, PartialEq, Eq)] |
19 | pub struct Runnable { | 26 | pub struct Runnable { |
20 | pub nav: NavigationTarget, | 27 | pub nav: NavigationTarget, |
21 | pub kind: RunnableKind, | 28 | pub kind: RunnableKind, |
22 | pub cfg: Option<CfgExpr>, | 29 | pub cfg: Option<CfgExpr>, |
23 | } | 30 | } |
24 | 31 | ||
25 | #[derive(Debug, Clone)] | 32 | #[derive(Debug, Clone, Hash, PartialEq, Eq)] |
26 | pub enum TestId { | 33 | pub enum TestId { |
27 | Name(String), | 34 | Name(String), |
28 | Path(String), | 35 | Path(String), |
@@ -37,7 +44,7 @@ impl fmt::Display for TestId { | |||
37 | } | 44 | } |
38 | } | 45 | } |
39 | 46 | ||
40 | #[derive(Debug, Clone)] | 47 | #[derive(Debug, Clone, Hash, PartialEq, Eq)] |
41 | pub enum RunnableKind { | 48 | pub enum RunnableKind { |
42 | Test { test_id: TestId, attr: TestAttr }, | 49 | Test { test_id: TestId, attr: TestAttr }, |
43 | TestMod { path: String }, | 50 | TestMod { path: String }, |
@@ -105,6 +112,105 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { | |||
105 | res | 112 | res |
106 | } | 113 | } |
107 | 114 | ||
115 | // Feature: Related Tests | ||
116 | // | ||
117 | // Provides a sneak peek of all tests where the current item is used. | ||
118 | // | ||
119 | // The simplest way to use this feature is via the context menu: | ||
120 | // - Right-click on the selected item. The context menu opens. | ||
121 | // - Select **Peek related tests** | ||
122 | // | ||
123 | // |=== | ||
124 | // | Editor | Action Name | ||
125 | // | ||
126 | // | VS Code | **Rust Analyzer: Peek related tests** | ||
127 | // |=== | ||
128 | pub(crate) fn related_tests( | ||
129 | db: &RootDatabase, | ||
130 | position: FilePosition, | ||
131 | search_scope: Option<SearchScope>, | ||
132 | ) -> Vec<Runnable> { | ||
133 | let sema = Semantics::new(db); | ||
134 | let mut res: FxHashSet<Runnable> = FxHashSet::default(); | ||
135 | |||
136 | find_related_tests(&sema, position, search_scope, &mut res); | ||
137 | |||
138 | res.into_iter().collect_vec() | ||
139 | } | ||
140 | |||
141 | fn find_related_tests( | ||
142 | sema: &Semantics<RootDatabase>, | ||
143 | position: FilePosition, | ||
144 | search_scope: Option<SearchScope>, | ||
145 | tests: &mut FxHashSet<Runnable>, | ||
146 | ) { | ||
147 | if let Some(refs) = references::find_all_refs(&sema, position, search_scope) { | ||
148 | for (file_id, refs) in refs.references { | ||
149 | let file = sema.parse(file_id); | ||
150 | let file = file.syntax(); | ||
151 | let functions = refs.iter().filter_map(|(range, _)| { | ||
152 | let token = file.token_at_offset(range.start()).next()?; | ||
153 | let token = sema.descend_into_macros(token); | ||
154 | let syntax = token.parent(); | ||
155 | syntax.ancestors().find_map(ast::Fn::cast) | ||
156 | }); | ||
157 | |||
158 | for fn_def in functions { | ||
159 | if let Some(runnable) = as_test_runnable(&sema, &fn_def) { | ||
160 | // direct test | ||
161 | tests.insert(runnable); | ||
162 | } else if let Some(module) = parent_test_module(&sema, &fn_def) { | ||
163 | // indirect test | ||
164 | find_related_tests_in_module(sema, &fn_def, &module, tests); | ||
165 | } | ||
166 | } | ||
167 | } | ||
168 | } | ||
169 | } | ||
170 | |||
171 | fn find_related_tests_in_module( | ||
172 | sema: &Semantics<RootDatabase>, | ||
173 | fn_def: &ast::Fn, | ||
174 | parent_module: &hir::Module, | ||
175 | tests: &mut FxHashSet<Runnable>, | ||
176 | ) { | ||
177 | if let Some(fn_name) = fn_def.name() { | ||
178 | let mod_source = parent_module.definition_source(sema.db); | ||
179 | let range = match mod_source.value { | ||
180 | hir::ModuleSource::Module(m) => m.syntax().text_range(), | ||
181 | hir::ModuleSource::BlockExpr(b) => b.syntax().text_range(), | ||
182 | hir::ModuleSource::SourceFile(f) => f.syntax().text_range(), | ||
183 | }; | ||
184 | |||
185 | let file_id = mod_source.file_id.original_file(sema.db); | ||
186 | let mod_scope = SearchScope::file_range(FileRange { file_id, range }); | ||
187 | let fn_pos = FilePosition { file_id, offset: fn_name.syntax().text_range().start() }; | ||
188 | find_related_tests(sema, fn_pos, Some(mod_scope), tests) | ||
189 | } | ||
190 | } | ||
191 | |||
192 | fn as_test_runnable(sema: &Semantics<RootDatabase>, fn_def: &ast::Fn) -> Option<Runnable> { | ||
193 | if test_related_attribute(&fn_def).is_some() { | ||
194 | let function = sema.to_def(fn_def)?; | ||
195 | runnable_fn(sema, function) | ||
196 | } else { | ||
197 | None | ||
198 | } | ||
199 | } | ||
200 | |||
201 | fn parent_test_module(sema: &Semantics<RootDatabase>, fn_def: &ast::Fn) -> Option<hir::Module> { | ||
202 | fn_def.syntax().ancestors().find_map(|node| { | ||
203 | let module = ast::Module::cast(node)?; | ||
204 | let module = sema.to_def(&module)?; | ||
205 | |||
206 | if has_test_function_or_multiple_test_submodules(sema, &module) { | ||
207 | Some(module) | ||
208 | } else { | ||
209 | None | ||
210 | } | ||
211 | }) | ||
212 | } | ||
213 | |||
108 | fn runnables_mod(sema: &Semantics<RootDatabase>, acc: &mut Vec<Runnable>, module: hir::Module) { | 214 | fn runnables_mod(sema: &Semantics<RootDatabase>, acc: &mut Vec<Runnable>, module: hir::Module) { |
109 | acc.extend(module.declarations(sema.db).into_iter().filter_map(|def| { | 215 | acc.extend(module.declarations(sema.db).into_iter().filter_map(|def| { |
110 | let runnable = match def { | 216 | let runnable = match def { |
@@ -234,11 +340,21 @@ fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Op | |||
234 | // FIXME: this also looks very wrong | 340 | // FIXME: this also looks very wrong |
235 | if let Some(assoc_def) = assoc_def { | 341 | if let Some(assoc_def) = assoc_def { |
236 | if let hir::AssocItemContainer::Impl(imp) = assoc_def.container(sema.db) { | 342 | if let hir::AssocItemContainer::Impl(imp) = assoc_def.container(sema.db) { |
237 | if let Some(adt) = imp.target_ty(sema.db).as_adt() { | 343 | let ty = imp.target_ty(sema.db); |
238 | let name = adt.name(sema.db).to_string(); | 344 | if let Some(adt) = ty.as_adt() { |
345 | let name = adt.name(sema.db); | ||
239 | let idx = path.rfind(':').map_or(0, |idx| idx + 1); | 346 | let idx = path.rfind(':').map_or(0, |idx| idx + 1); |
240 | let (prefix, suffix) = path.split_at(idx); | 347 | let (prefix, suffix) = path.split_at(idx); |
241 | return format!("{}{}::{}", prefix, name, suffix); | 348 | let mut ty_params = ty.type_parameters().peekable(); |
349 | let params = if ty_params.peek().is_some() { | ||
350 | format!( | ||
351 | "<{}>", | ||
352 | ty_params.format_with(", ", |ty, cb| cb(&ty.display(sema.db))) | ||
353 | ) | ||
354 | } else { | ||
355 | String::new() | ||
356 | }; | ||
357 | return format!("{}{}{}::{}", prefix, name, params, suffix); | ||
242 | } | 358 | } |
243 | } | 359 | } |
244 | } | 360 | } |
@@ -256,7 +372,7 @@ fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Op | |||
256 | Some(res) | 372 | Some(res) |
257 | } | 373 | } |
258 | 374 | ||
259 | #[derive(Debug, Copy, Clone)] | 375 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] |
260 | pub struct TestAttr { | 376 | pub struct TestAttr { |
261 | pub ignore: bool, | 377 | pub ignore: bool, |
262 | } | 378 | } |
@@ -349,6 +465,12 @@ mod tests { | |||
349 | ); | 465 | ); |
350 | } | 466 | } |
351 | 467 | ||
468 | fn check_tests(ra_fixture: &str, expect: Expect) { | ||
469 | let (analysis, position) = fixture::position(ra_fixture); | ||
470 | let tests = analysis.related_tests(position, None).unwrap(); | ||
471 | expect.assert_debug_eq(&tests); | ||
472 | } | ||
473 | |||
352 | #[test] | 474 | #[test] |
353 | fn test_runnables() { | 475 | fn test_runnables() { |
354 | check( | 476 | check( |
@@ -1074,4 +1196,261 @@ mod tests { | |||
1074 | "#]], | 1196 | "#]], |
1075 | ); | 1197 | ); |
1076 | } | 1198 | } |
1199 | |||
1200 | #[test] | ||
1201 | fn find_no_tests() { | ||
1202 | check_tests( | ||
1203 | r#" | ||
1204 | //- /lib.rs | ||
1205 | fn foo$0() { }; | ||
1206 | "#, | ||
1207 | expect![[r#" | ||
1208 | [] | ||
1209 | "#]], | ||
1210 | ); | ||
1211 | } | ||
1212 | |||
1213 | #[test] | ||
1214 | fn find_direct_fn_test() { | ||
1215 | check_tests( | ||
1216 | r#" | ||
1217 | //- /lib.rs | ||
1218 | fn foo$0() { }; | ||
1219 | |||
1220 | mod tests { | ||
1221 | #[test] | ||
1222 | fn foo_test() { | ||
1223 | super::foo() | ||
1224 | } | ||
1225 | } | ||
1226 | "#, | ||
1227 | expect![[r#" | ||
1228 | [ | ||
1229 | Runnable { | ||
1230 | nav: NavigationTarget { | ||
1231 | file_id: FileId( | ||
1232 | 0, | ||
1233 | ), | ||
1234 | full_range: 31..85, | ||
1235 | focus_range: 46..54, | ||
1236 | name: "foo_test", | ||
1237 | kind: Function, | ||
1238 | }, | ||
1239 | kind: Test { | ||
1240 | test_id: Path( | ||
1241 | "tests::foo_test", | ||
1242 | ), | ||
1243 | attr: TestAttr { | ||
1244 | ignore: false, | ||
1245 | }, | ||
1246 | }, | ||
1247 | cfg: None, | ||
1248 | }, | ||
1249 | ] | ||
1250 | "#]], | ||
1251 | ); | ||
1252 | } | ||
1253 | |||
1254 | #[test] | ||
1255 | fn find_direct_struct_test() { | ||
1256 | check_tests( | ||
1257 | r#" | ||
1258 | //- /lib.rs | ||
1259 | struct Fo$0o; | ||
1260 | fn foo(arg: &Foo) { }; | ||
1261 | |||
1262 | mod tests { | ||
1263 | use super::*; | ||
1264 | |||
1265 | #[test] | ||
1266 | fn foo_test() { | ||
1267 | foo(Foo); | ||
1268 | } | ||
1269 | } | ||
1270 | "#, | ||
1271 | expect![[r#" | ||
1272 | [ | ||
1273 | Runnable { | ||
1274 | nav: NavigationTarget { | ||
1275 | file_id: FileId( | ||
1276 | 0, | ||
1277 | ), | ||
1278 | full_range: 71..122, | ||
1279 | focus_range: 86..94, | ||
1280 | name: "foo_test", | ||
1281 | kind: Function, | ||
1282 | }, | ||
1283 | kind: Test { | ||
1284 | test_id: Path( | ||
1285 | "tests::foo_test", | ||
1286 | ), | ||
1287 | attr: TestAttr { | ||
1288 | ignore: false, | ||
1289 | }, | ||
1290 | }, | ||
1291 | cfg: None, | ||
1292 | }, | ||
1293 | ] | ||
1294 | "#]], | ||
1295 | ); | ||
1296 | } | ||
1297 | |||
1298 | #[test] | ||
1299 | fn find_indirect_fn_test() { | ||
1300 | check_tests( | ||
1301 | r#" | ||
1302 | //- /lib.rs | ||
1303 | fn foo$0() { }; | ||
1304 | |||
1305 | mod tests { | ||
1306 | use super::foo; | ||
1307 | |||
1308 | fn check1() { | ||
1309 | check2() | ||
1310 | } | ||
1311 | |||
1312 | fn check2() { | ||
1313 | foo() | ||
1314 | } | ||
1315 | |||
1316 | #[test] | ||
1317 | fn foo_test() { | ||
1318 | check1() | ||
1319 | } | ||
1320 | } | ||
1321 | "#, | ||
1322 | expect![[r#" | ||
1323 | [ | ||
1324 | Runnable { | ||
1325 | nav: NavigationTarget { | ||
1326 | file_id: FileId( | ||
1327 | 0, | ||
1328 | ), | ||
1329 | full_range: 133..183, | ||
1330 | focus_range: 148..156, | ||
1331 | name: "foo_test", | ||
1332 | kind: Function, | ||
1333 | }, | ||
1334 | kind: Test { | ||
1335 | test_id: Path( | ||
1336 | "tests::foo_test", | ||
1337 | ), | ||
1338 | attr: TestAttr { | ||
1339 | ignore: false, | ||
1340 | }, | ||
1341 | }, | ||
1342 | cfg: None, | ||
1343 | }, | ||
1344 | ] | ||
1345 | "#]], | ||
1346 | ); | ||
1347 | } | ||
1348 | |||
1349 | #[test] | ||
1350 | fn tests_are_unique() { | ||
1351 | check_tests( | ||
1352 | r#" | ||
1353 | //- /lib.rs | ||
1354 | fn foo$0() { }; | ||
1355 | |||
1356 | mod tests { | ||
1357 | use super::foo; | ||
1358 | |||
1359 | #[test] | ||
1360 | fn foo_test() { | ||
1361 | foo(); | ||
1362 | foo(); | ||
1363 | } | ||
1364 | |||
1365 | #[test] | ||
1366 | fn foo2_test() { | ||
1367 | foo(); | ||
1368 | foo(); | ||
1369 | } | ||
1370 | |||
1371 | } | ||
1372 | "#, | ||
1373 | expect![[r#" | ||
1374 | [ | ||
1375 | Runnable { | ||
1376 | nav: NavigationTarget { | ||
1377 | file_id: FileId( | ||
1378 | 0, | ||
1379 | ), | ||
1380 | full_range: 52..115, | ||
1381 | focus_range: 67..75, | ||
1382 | name: "foo_test", | ||
1383 | kind: Function, | ||
1384 | }, | ||
1385 | kind: Test { | ||
1386 | test_id: Path( | ||
1387 | "tests::foo_test", | ||
1388 | ), | ||
1389 | attr: TestAttr { | ||
1390 | ignore: false, | ||
1391 | }, | ||
1392 | }, | ||
1393 | cfg: None, | ||
1394 | }, | ||
1395 | Runnable { | ||
1396 | nav: NavigationTarget { | ||
1397 | file_id: FileId( | ||
1398 | 0, | ||
1399 | ), | ||
1400 | full_range: 121..185, | ||
1401 | focus_range: 136..145, | ||
1402 | name: "foo2_test", | ||
1403 | kind: Function, | ||
1404 | }, | ||
1405 | kind: Test { | ||
1406 | test_id: Path( | ||
1407 | "tests::foo2_test", | ||
1408 | ), | ||
1409 | attr: TestAttr { | ||
1410 | ignore: false, | ||
1411 | }, | ||
1412 | }, | ||
1413 | cfg: None, | ||
1414 | }, | ||
1415 | ] | ||
1416 | "#]], | ||
1417 | ); | ||
1418 | } | ||
1419 | |||
1420 | #[test] | ||
1421 | fn doc_test_type_params() { | ||
1422 | check( | ||
1423 | r#" | ||
1424 | //- /lib.rs | ||
1425 | $0 | ||
1426 | struct Foo<T, U>; | ||
1427 | |||
1428 | impl<T, U> Foo<T, U> { | ||
1429 | /// ```rust | ||
1430 | /// ```` | ||
1431 | fn t() {} | ||
1432 | } | ||
1433 | "#, | ||
1434 | &[&DOCTEST], | ||
1435 | expect![[r#" | ||
1436 | [ | ||
1437 | Runnable { | ||
1438 | nav: NavigationTarget { | ||
1439 | file_id: FileId( | ||
1440 | 0, | ||
1441 | ), | ||
1442 | full_range: 47..85, | ||
1443 | name: "t", | ||
1444 | }, | ||
1445 | kind: DocTest { | ||
1446 | test_id: Path( | ||
1447 | "Foo<T, U>::t", | ||
1448 | ), | ||
1449 | }, | ||
1450 | cfg: None, | ||
1451 | }, | ||
1452 | ] | ||
1453 | "#]], | ||
1454 | ); | ||
1455 | } | ||
1077 | } | 1456 | } |