aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/goto_definition.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/goto_definition.rs')
-rw-r--r--crates/ra_ide/src/goto_definition.rs696
1 files changed, 696 insertions, 0 deletions
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
new file mode 100644
index 000000000..c10a6c844
--- /dev/null
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -0,0 +1,696 @@
1//! FIXME: write short doc here
2
3use hir::{db::AstDatabase, Source};
4use ra_syntax::{
5 ast::{self, DocCommentsOwner},
6 match_ast, AstNode, SyntaxNode,
7};
8
9use crate::{
10 db::RootDatabase,
11 display::{ShortLabel, ToNav},
12 expand::descend_into_macros,
13 references::{classify_name_ref, NameKind::*},
14 FilePosition, NavigationTarget, RangeInfo,
15};
16
17pub(crate) fn goto_definition(
18 db: &RootDatabase,
19 position: FilePosition,
20) -> Option<RangeInfo<Vec<NavigationTarget>>> {
21 let file = db.parse_or_expand(position.file_id.into())?;
22 let token = file.token_at_offset(position.offset).filter(|it| !it.kind().is_trivia()).next()?;
23 let token = descend_into_macros(db, position.file_id, token);
24
25 let res = match_ast! {
26 match (token.value.parent()) {
27 ast::NameRef(name_ref) => {
28 let navs = reference_definition(db, token.with_value(&name_ref)).to_vec();
29 RangeInfo::new(name_ref.syntax().text_range(), navs.to_vec())
30 },
31 ast::Name(name) => {
32 let navs = name_definition(db, token.with_value(&name))?;
33 RangeInfo::new(name.syntax().text_range(), navs)
34
35 },
36 _ => return None,
37 }
38 };
39
40 Some(res)
41}
42
43#[derive(Debug)]
44pub(crate) enum ReferenceResult {
45 Exact(NavigationTarget),
46 Approximate(Vec<NavigationTarget>),
47}
48
49impl ReferenceResult {
50 fn to_vec(self) -> Vec<NavigationTarget> {
51 use self::ReferenceResult::*;
52 match self {
53 Exact(target) => vec![target],
54 Approximate(vec) => vec,
55 }
56 }
57}
58
59pub(crate) fn reference_definition(
60 db: &RootDatabase,
61 name_ref: Source<&ast::NameRef>,
62) -> ReferenceResult {
63 use self::ReferenceResult::*;
64
65 let name_kind = classify_name_ref(db, name_ref).map(|d| d.kind);
66 match name_kind {
67 Some(Macro(mac)) => return Exact(mac.to_nav(db)),
68 Some(Field(field)) => return Exact(field.to_nav(db)),
69 Some(AssocItem(assoc)) => return Exact(assoc.to_nav(db)),
70 Some(Def(def)) => match NavigationTarget::from_def(db, def) {
71 Some(nav) => return Exact(nav),
72 None => return Approximate(vec![]),
73 },
74 Some(SelfType(imp)) => {
75 // FIXME: ideally, this should point to the type in the impl, and
76 // not at the whole impl. And goto **type** definition should bring
77 // us to the actual type
78 return Exact(imp.to_nav(db));
79 }
80 Some(Local(local)) => return Exact(local.to_nav(db)),
81 Some(GenericParam(_)) => {
82 // FIXME: go to the generic param def
83 }
84 None => {}
85 };
86
87 // Fallback index based approach:
88 let navs = crate::symbol_index::index_resolve(db, name_ref.value)
89 .into_iter()
90 .map(|s| s.to_nav(db))
91 .collect();
92 Approximate(navs)
93}
94
95pub(crate) fn name_definition(
96 db: &RootDatabase,
97 name: Source<&ast::Name>,
98) -> Option<Vec<NavigationTarget>> {
99 let parent = name.value.syntax().parent()?;
100
101 if let Some(module) = ast::Module::cast(parent.clone()) {
102 if module.has_semi() {
103 let src = name.with_value(module);
104 if let Some(child_module) = hir::Module::from_declaration(db, src) {
105 let nav = child_module.to_nav(db);
106 return Some(vec![nav]);
107 }
108 }
109 }
110
111 if let Some(nav) = named_target(db, name.with_value(&parent)) {
112 return Some(vec![nav]);
113 }
114
115 None
116}
117
118fn named_target(db: &RootDatabase, node: Source<&SyntaxNode>) -> Option<NavigationTarget> {
119 match_ast! {
120 match (node.value) {
121 ast::StructDef(it) => {
122 Some(NavigationTarget::from_named(
123 db,
124 node.with_value(&it),
125 it.doc_comment_text(),
126 it.short_label(),
127 ))
128 },
129 ast::EnumDef(it) => {
130 Some(NavigationTarget::from_named(
131 db,
132 node.with_value(&it),
133 it.doc_comment_text(),
134 it.short_label(),
135 ))
136 },
137 ast::EnumVariant(it) => {
138 Some(NavigationTarget::from_named(
139 db,
140 node.with_value(&it),
141 it.doc_comment_text(),
142 it.short_label(),
143 ))
144 },
145 ast::FnDef(it) => {
146 Some(NavigationTarget::from_named(
147 db,
148 node.with_value(&it),
149 it.doc_comment_text(),
150 it.short_label(),
151 ))
152 },
153 ast::TypeAliasDef(it) => {
154 Some(NavigationTarget::from_named(
155 db,
156 node.with_value(&it),
157 it.doc_comment_text(),
158 it.short_label(),
159 ))
160 },
161 ast::ConstDef(it) => {
162 Some(NavigationTarget::from_named(
163 db,
164 node.with_value(&it),
165 it.doc_comment_text(),
166 it.short_label(),
167 ))
168 },
169 ast::StaticDef(it) => {
170 Some(NavigationTarget::from_named(
171 db,
172 node.with_value(&it),
173 it.doc_comment_text(),
174 it.short_label(),
175 ))
176 },
177 ast::TraitDef(it) => {
178 Some(NavigationTarget::from_named(
179 db,
180 node.with_value(&it),
181 it.doc_comment_text(),
182 it.short_label(),
183 ))
184 },
185 ast::RecordFieldDef(it) => {
186 Some(NavigationTarget::from_named(
187 db,
188 node.with_value(&it),
189 it.doc_comment_text(),
190 it.short_label(),
191 ))
192 },
193 ast::Module(it) => {
194 Some(NavigationTarget::from_named(
195 db,
196 node.with_value(&it),
197 it.doc_comment_text(),
198 it.short_label(),
199 ))
200 },
201 ast::MacroCall(it) => {
202 Some(NavigationTarget::from_named(
203 db,
204 node.with_value(&it),
205 it.doc_comment_text(),
206 None,
207 ))
208 },
209 _ => None,
210 }
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use test_utils::covers;
217
218 use crate::mock_analysis::analysis_and_position;
219
220 fn check_goto(fixture: &str, expected: &str) {
221 let (analysis, pos) = analysis_and_position(fixture);
222
223 let mut navs = analysis.goto_definition(pos).unwrap().unwrap().info;
224 assert_eq!(navs.len(), 1);
225 let nav = navs.pop().unwrap();
226 nav.assert_match(expected);
227 }
228
229 #[test]
230 fn goto_definition_works_in_items() {
231 check_goto(
232 "
233 //- /lib.rs
234 struct Foo;
235 enum E { X(Foo<|>) }
236 ",
237 "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
238 );
239 }
240
241 #[test]
242 fn goto_definition_resolves_correct_name() {
243 check_goto(
244 "
245 //- /lib.rs
246 use a::Foo;
247 mod a;
248 mod b;
249 enum E { X(Foo<|>) }
250 //- /a.rs
251 struct Foo;
252 //- /b.rs
253 struct Foo;
254 ",
255 "Foo STRUCT_DEF FileId(2) [0; 11) [7; 10)",
256 );
257 }
258
259 #[test]
260 fn goto_definition_works_for_module_declaration() {
261 check_goto(
262 "
263 //- /lib.rs
264 mod <|>foo;
265 //- /foo.rs
266 // empty
267 ",
268 "foo SOURCE_FILE FileId(2) [0; 10)",
269 );
270
271 check_goto(
272 "
273 //- /lib.rs
274 mod <|>foo;
275 //- /foo/mod.rs
276 // empty
277 ",
278 "foo SOURCE_FILE FileId(2) [0; 10)",
279 );
280 }
281
282 #[test]
283 fn goto_definition_works_for_macros() {
284 covers!(goto_definition_works_for_macros);
285 check_goto(
286 "
287 //- /lib.rs
288 macro_rules! foo {
289 () => {
290 {}
291 };
292 }
293
294 fn bar() {
295 <|>foo!();
296 }
297 ",
298 "foo MACRO_CALL FileId(1) [0; 50) [13; 16)",
299 );
300 }
301
302 #[test]
303 fn goto_definition_works_for_macros_from_other_crates() {
304 covers!(goto_definition_works_for_macros);
305 check_goto(
306 "
307 //- /lib.rs
308 use foo::foo;
309 fn bar() {
310 <|>foo!();
311 }
312
313 //- /foo/lib.rs
314 #[macro_export]
315 macro_rules! foo {
316 () => {
317 {}
318 };
319 }
320 ",
321 "foo MACRO_CALL FileId(2) [0; 66) [29; 32)",
322 );
323 }
324
325 #[test]
326 fn goto_definition_works_for_macros_in_use_tree() {
327 check_goto(
328 "
329 //- /lib.rs
330 use foo::foo<|>;
331
332 //- /foo/lib.rs
333 #[macro_export]
334 macro_rules! foo {
335 () => {
336 {}
337 };
338 }
339 ",
340 "foo MACRO_CALL FileId(2) [0; 66) [29; 32)",
341 );
342 }
343
344 #[test]
345 fn goto_definition_works_for_macro_defined_fn_with_arg() {
346 check_goto(
347 "
348 //- /lib.rs
349 macro_rules! define_fn {
350 ($name:ident) => (fn $name() {})
351 }
352
353 define_fn!(
354 foo
355 )
356
357 fn bar() {
358 <|>foo();
359 }
360 ",
361 "foo FN_DEF FileId(1) [80; 83) [80; 83)",
362 );
363 }
364
365 #[test]
366 fn goto_definition_works_for_macro_defined_fn_no_arg() {
367 check_goto(
368 "
369 //- /lib.rs
370 macro_rules! define_fn {
371 () => (fn foo() {})
372 }
373
374 define_fn!();
375
376 fn bar() {
377 <|>foo();
378 }
379 ",
380 "foo FN_DEF FileId(1) [39; 42) [39; 42)",
381 );
382 }
383
384 #[test]
385 fn goto_definition_works_for_methods() {
386 covers!(goto_definition_works_for_methods);
387 check_goto(
388 "
389 //- /lib.rs
390 struct Foo;
391 impl Foo {
392 fn frobnicate(&self) { }
393 }
394
395 fn bar(foo: &Foo) {
396 foo.frobnicate<|>();
397 }
398 ",
399 "frobnicate FN_DEF FileId(1) [27; 52) [30; 40)",
400 );
401 }
402
403 #[test]
404 fn goto_definition_works_for_fields() {
405 covers!(goto_definition_works_for_fields);
406 check_goto(
407 "
408 //- /lib.rs
409 struct Foo {
410 spam: u32,
411 }
412
413 fn bar(foo: &Foo) {
414 foo.spam<|>;
415 }
416 ",
417 "spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)",
418 );
419 }
420
421 #[test]
422 fn goto_definition_works_for_record_fields() {
423 covers!(goto_definition_works_for_record_fields);
424 check_goto(
425 "
426 //- /lib.rs
427 struct Foo {
428 spam: u32,
429 }
430
431 fn bar() -> Foo {
432 Foo {
433 spam<|>: 0,
434 }
435 }
436 ",
437 "spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)",
438 );
439 }
440
441 #[test]
442 fn goto_definition_works_for_ufcs_inherent_methods() {
443 check_goto(
444 "
445 //- /lib.rs
446 struct Foo;
447 impl Foo {
448 fn frobnicate() { }
449 }
450
451 fn bar(foo: &Foo) {
452 Foo::frobnicate<|>();
453 }
454 ",
455 "frobnicate FN_DEF FileId(1) [27; 47) [30; 40)",
456 );
457 }
458
459 #[test]
460 fn goto_definition_works_for_ufcs_trait_methods_through_traits() {
461 check_goto(
462 "
463 //- /lib.rs
464 trait Foo {
465 fn frobnicate();
466 }
467
468 fn bar() {
469 Foo::frobnicate<|>();
470 }
471 ",
472 "frobnicate FN_DEF FileId(1) [16; 32) [19; 29)",
473 );
474 }
475
476 #[test]
477 fn goto_definition_works_for_ufcs_trait_methods_through_self() {
478 check_goto(
479 "
480 //- /lib.rs
481 struct Foo;
482 trait Trait {
483 fn frobnicate();
484 }
485 impl Trait for Foo {}
486
487 fn bar() {
488 Foo::frobnicate<|>();
489 }
490 ",
491 "frobnicate FN_DEF FileId(1) [30; 46) [33; 43)",
492 );
493 }
494
495 #[test]
496 fn goto_definition_on_self() {
497 check_goto(
498 "
499 //- /lib.rs
500 struct Foo;
501 impl Foo {
502 pub fn new() -> Self {
503 Self<|> {}
504 }
505 }
506 ",
507 "impl IMPL_BLOCK FileId(1) [12; 73)",
508 );
509
510 check_goto(
511 "
512 //- /lib.rs
513 struct Foo;
514 impl Foo {
515 pub fn new() -> Self<|> {
516 Self {}
517 }
518 }
519 ",
520 "impl IMPL_BLOCK FileId(1) [12; 73)",
521 );
522
523 check_goto(
524 "
525 //- /lib.rs
526 enum Foo { A }
527 impl Foo {
528 pub fn new() -> Self<|> {
529 Foo::A
530 }
531 }
532 ",
533 "impl IMPL_BLOCK FileId(1) [15; 75)",
534 );
535
536 check_goto(
537 "
538 //- /lib.rs
539 enum Foo { A }
540 impl Foo {
541 pub fn thing(a: &Self<|>) {
542 }
543 }
544 ",
545 "impl IMPL_BLOCK FileId(1) [15; 62)",
546 );
547 }
548
549 #[test]
550 fn goto_definition_on_self_in_trait_impl() {
551 check_goto(
552 "
553 //- /lib.rs
554 struct Foo;
555 trait Make {
556 fn new() -> Self;
557 }
558 impl Make for Foo {
559 fn new() -> Self {
560 Self<|> {}
561 }
562 }
563 ",
564 "impl IMPL_BLOCK FileId(1) [49; 115)",
565 );
566
567 check_goto(
568 "
569 //- /lib.rs
570 struct Foo;
571 trait Make {
572 fn new() -> Self;
573 }
574 impl Make for Foo {
575 fn new() -> Self<|> {
576 Self {}
577 }
578 }
579 ",
580 "impl IMPL_BLOCK FileId(1) [49; 115)",
581 );
582 }
583
584 #[test]
585 fn goto_definition_works_when_used_on_definition_name_itself() {
586 check_goto(
587 "
588 //- /lib.rs
589 struct Foo<|> { value: u32 }
590 ",
591 "Foo STRUCT_DEF FileId(1) [0; 25) [7; 10)",
592 );
593
594 check_goto(
595 r#"
596 //- /lib.rs
597 struct Foo {
598 field<|>: string,
599 }
600 "#,
601 "field RECORD_FIELD_DEF FileId(1) [17; 30) [17; 22)",
602 );
603
604 check_goto(
605 "
606 //- /lib.rs
607 fn foo_test<|>() {
608 }
609 ",
610 "foo_test FN_DEF FileId(1) [0; 17) [3; 11)",
611 );
612
613 check_goto(
614 "
615 //- /lib.rs
616 enum Foo<|> {
617 Variant,
618 }
619 ",
620 "Foo ENUM_DEF FileId(1) [0; 25) [5; 8)",
621 );
622
623 check_goto(
624 "
625 //- /lib.rs
626 enum Foo {
627 Variant1,
628 Variant2<|>,
629 Variant3,
630 }
631 ",
632 "Variant2 ENUM_VARIANT FileId(1) [29; 37) [29; 37)",
633 );
634
635 check_goto(
636 r#"
637 //- /lib.rs
638 static inner<|>: &str = "";
639 "#,
640 "inner STATIC_DEF FileId(1) [0; 24) [7; 12)",
641 );
642
643 check_goto(
644 r#"
645 //- /lib.rs
646 const inner<|>: &str = "";
647 "#,
648 "inner CONST_DEF FileId(1) [0; 23) [6; 11)",
649 );
650
651 check_goto(
652 r#"
653 //- /lib.rs
654 type Thing<|> = Option<()>;
655 "#,
656 "Thing TYPE_ALIAS_DEF FileId(1) [0; 24) [5; 10)",
657 );
658
659 check_goto(
660 r#"
661 //- /lib.rs
662 trait Foo<|> {
663 }
664 "#,
665 "Foo TRAIT_DEF FileId(1) [0; 13) [6; 9)",
666 );
667
668 check_goto(
669 r#"
670 //- /lib.rs
671 mod bar<|> {
672 }
673 "#,
674 "bar MODULE FileId(1) [0; 11) [4; 7)",
675 );
676 }
677
678 #[test]
679 fn goto_from_macro() {
680 check_goto(
681 "
682 //- /lib.rs
683 macro_rules! id {
684 ($($tt:tt)*) => { $($tt)* }
685 }
686 fn foo() {}
687 id! {
688 fn bar() {
689 fo<|>o();
690 }
691 }
692 ",
693 "foo FN_DEF FileId(1) [52; 63) [55; 58)",
694 );
695 }
696}