aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/goto_definition.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src/goto_definition.rs')
-rw-r--r--crates/ide/src/goto_definition.rs989
1 files changed, 989 insertions, 0 deletions
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
new file mode 100644
index 000000000..15e9b7fad
--- /dev/null
+++ b/crates/ide/src/goto_definition.rs
@@ -0,0 +1,989 @@
1use hir::Semantics;
2use ide_db::{
3 defs::{classify_name, classify_name_ref},
4 symbol_index, RootDatabase,
5};
6use syntax::{
7 ast::{self},
8 match_ast, AstNode,
9 SyntaxKind::*,
10 SyntaxToken, TokenAtOffset, T,
11};
12
13use crate::{
14 display::{ToNav, TryToNav},
15 FilePosition, NavigationTarget, RangeInfo,
16};
17
18// Feature: Go to Definition
19//
20// Navigates to the definition of an identifier.
21//
22// |===
23// | Editor | Shortcut
24//
25// | VS Code | kbd:[F12]
26// |===
27pub(crate) fn goto_definition(
28 db: &RootDatabase,
29 position: FilePosition,
30) -> Option<RangeInfo<Vec<NavigationTarget>>> {
31 let sema = Semantics::new(db);
32 let file = sema.parse(position.file_id).syntax().clone();
33 let original_token = pick_best(file.token_at_offset(position.offset))?;
34 let token = sema.descend_into_macros(original_token.clone());
35 let parent = token.parent();
36
37 let nav_targets = match_ast! {
38 match parent {
39 ast::NameRef(name_ref) => {
40 reference_definition(&sema, &name_ref).to_vec()
41 },
42 ast::Name(name) => {
43 let def = classify_name(&sema, &name)?.definition(sema.db);
44 let nav = def.try_to_nav(sema.db)?;
45 vec![nav]
46 },
47 _ => return None,
48 }
49 };
50
51 Some(RangeInfo::new(original_token.text_range(), nav_targets))
52}
53
54fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
55 return tokens.max_by_key(priority);
56 fn priority(n: &SyntaxToken) -> usize {
57 match n.kind() {
58 IDENT | INT_NUMBER | T![self] => 2,
59 kind if kind.is_trivia() => 0,
60 _ => 1,
61 }
62 }
63}
64
65#[derive(Debug)]
66pub(crate) enum ReferenceResult {
67 Exact(NavigationTarget),
68 Approximate(Vec<NavigationTarget>),
69}
70
71impl ReferenceResult {
72 fn to_vec(self) -> Vec<NavigationTarget> {
73 match self {
74 ReferenceResult::Exact(target) => vec![target],
75 ReferenceResult::Approximate(vec) => vec,
76 }
77 }
78}
79
80pub(crate) fn reference_definition(
81 sema: &Semantics<RootDatabase>,
82 name_ref: &ast::NameRef,
83) -> ReferenceResult {
84 let name_kind = classify_name_ref(sema, name_ref);
85 if let Some(def) = name_kind {
86 let def = def.definition(sema.db);
87 return match def.try_to_nav(sema.db) {
88 Some(nav) => ReferenceResult::Exact(nav),
89 None => ReferenceResult::Approximate(Vec::new()),
90 };
91 }
92
93 // Fallback index based approach:
94 let navs = symbol_index::index_resolve(sema.db, name_ref)
95 .into_iter()
96 .map(|s| s.to_nav(sema.db))
97 .collect();
98 ReferenceResult::Approximate(navs)
99}
100
101#[cfg(test)]
102mod tests {
103 use base_db::FileRange;
104 use syntax::{TextRange, TextSize};
105
106 use crate::mock_analysis::MockAnalysis;
107
108 fn check(ra_fixture: &str) {
109 let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture);
110 let (mut expected, data) = mock.annotation();
111 let analysis = mock.analysis();
112 match data.as_str() {
113 "" => (),
114 "file" => {
115 expected.range =
116 TextRange::up_to(TextSize::of(&*analysis.file_text(expected.file_id).unwrap()))
117 }
118 data => panic!("bad data: {}", data),
119 }
120
121 let mut navs =
122 analysis.goto_definition(position).unwrap().expect("no definition found").info;
123 if navs.len() == 0 {
124 panic!("unresolved reference")
125 }
126 assert_eq!(navs.len(), 1);
127
128 let nav = navs.pop().unwrap();
129 assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() });
130 }
131
132 #[test]
133 fn goto_def_for_extern_crate() {
134 check(
135 r#"
136 //- /main.rs
137 extern crate std<|>;
138 //- /std/lib.rs
139 // empty
140 //^ file
141 "#,
142 )
143 }
144
145 #[test]
146 fn goto_def_for_renamed_extern_crate() {
147 check(
148 r#"
149 //- /main.rs
150 extern crate std as abc<|>;
151 //- /std/lib.rs
152 // empty
153 //^ file
154 "#,
155 )
156 }
157
158 #[test]
159 fn goto_def_in_items() {
160 check(
161 r#"
162struct Foo;
163 //^^^
164enum E { X(Foo<|>) }
165"#,
166 );
167 }
168
169 #[test]
170 fn goto_def_at_start_of_item() {
171 check(
172 r#"
173struct Foo;
174 //^^^
175enum E { X(<|>Foo) }
176"#,
177 );
178 }
179
180 #[test]
181 fn goto_definition_resolves_correct_name() {
182 check(
183 r#"
184//- /lib.rs
185use a::Foo;
186mod a;
187mod b;
188enum E { X(Foo<|>) }
189
190//- /a.rs
191struct Foo;
192 //^^^
193//- /b.rs
194struct Foo;
195"#,
196 );
197 }
198
199 #[test]
200 fn goto_def_for_module_declaration() {
201 check(
202 r#"
203//- /lib.rs
204mod <|>foo;
205
206//- /foo.rs
207// empty
208//^ file
209"#,
210 );
211
212 check(
213 r#"
214//- /lib.rs
215mod <|>foo;
216
217//- /foo/mod.rs
218// empty
219//^ file
220"#,
221 );
222 }
223
224 #[test]
225 fn goto_def_for_macros() {
226 check(
227 r#"
228macro_rules! foo { () => { () } }
229 //^^^
230fn bar() {
231 <|>foo!();
232}
233"#,
234 );
235 }
236
237 #[test]
238 fn goto_def_for_macros_from_other_crates() {
239 check(
240 r#"
241//- /lib.rs
242use foo::foo;
243fn bar() {
244 <|>foo!();
245}
246
247//- /foo/lib.rs
248#[macro_export]
249macro_rules! foo { () => { () } }
250 //^^^
251"#,
252 );
253 }
254
255 #[test]
256 fn goto_def_for_macros_in_use_tree() {
257 check(
258 r#"
259//- /lib.rs
260use foo::foo<|>;
261
262//- /foo/lib.rs
263#[macro_export]
264macro_rules! foo { () => { () } }
265 //^^^
266"#,
267 );
268 }
269
270 #[test]
271 fn goto_def_for_macro_defined_fn_with_arg() {
272 check(
273 r#"
274//- /lib.rs
275macro_rules! define_fn {
276 ($name:ident) => (fn $name() {})
277}
278
279define_fn!(foo);
280 //^^^
281
282fn bar() {
283 <|>foo();
284}
285"#,
286 );
287 }
288
289 #[test]
290 fn goto_def_for_macro_defined_fn_no_arg() {
291 check(
292 r#"
293//- /lib.rs
294macro_rules! define_fn {
295 () => (fn foo() {})
296}
297
298 define_fn!();
299//^^^^^^^^^^^^^
300
301fn bar() {
302 <|>foo();
303}
304"#,
305 );
306 }
307
308 #[test]
309 fn goto_definition_works_for_macro_inside_pattern() {
310 check(
311 r#"
312//- /lib.rs
313macro_rules! foo {() => {0}}
314 //^^^
315
316fn bar() {
317 match (0,1) {
318 (<|>foo!(), _) => {}
319 }
320}
321"#,
322 );
323 }
324
325 #[test]
326 fn goto_definition_works_for_macro_inside_match_arm_lhs() {
327 check(
328 r#"
329//- /lib.rs
330macro_rules! foo {() => {0}}
331 //^^^
332fn bar() {
333 match 0 {
334 <|>foo!() => {}
335 }
336}
337"#,
338 );
339 }
340
341 #[test]
342 fn goto_def_for_use_alias() {
343 check(
344 r#"
345//- /lib.rs
346use foo as bar<|>;
347
348//- /foo/lib.rs
349// empty
350//^ file
351"#,
352 );
353 }
354
355 #[test]
356 fn goto_def_for_use_alias_foo_macro() {
357 check(
358 r#"
359//- /lib.rs
360use foo::foo as bar<|>;
361
362//- /foo/lib.rs
363#[macro_export]
364macro_rules! foo { () => { () } }
365 //^^^
366"#,
367 );
368 }
369
370 #[test]
371 fn goto_def_for_methods() {
372 check(
373 r#"
374//- /lib.rs
375struct Foo;
376impl Foo {
377 fn frobnicate(&self) { }
378 //^^^^^^^^^^
379}
380
381fn bar(foo: &Foo) {
382 foo.frobnicate<|>();
383}
384"#,
385 );
386 }
387
388 #[test]
389 fn goto_def_for_fields() {
390 check(
391 r#"
392struct Foo {
393 spam: u32,
394} //^^^^
395
396fn bar(foo: &Foo) {
397 foo.spam<|>;
398}
399"#,
400 );
401 }
402
403 #[test]
404 fn goto_def_for_record_fields() {
405 check(
406 r#"
407//- /lib.rs
408struct Foo {
409 spam: u32,
410} //^^^^
411
412fn bar() -> Foo {
413 Foo {
414 spam<|>: 0,
415 }
416}
417"#,
418 );
419 }
420
421 #[test]
422 fn goto_def_for_record_pat_fields() {
423 check(
424 r#"
425//- /lib.rs
426struct Foo {
427 spam: u32,
428} //^^^^
429
430fn bar(foo: Foo) -> Foo {
431 let Foo { spam<|>: _, } = foo
432}
433"#,
434 );
435 }
436
437 #[test]
438 fn goto_def_for_record_fields_macros() {
439 check(
440 r"
441macro_rules! m { () => { 92 };}
442struct Foo { spam: u32 }
443 //^^^^
444
445fn bar() -> Foo {
446 Foo { spam<|>: m!() }
447}
448",
449 );
450 }
451
452 #[test]
453 fn goto_for_tuple_fields() {
454 check(
455 r#"
456struct Foo(u32);
457 //^^^
458
459fn bar() {
460 let foo = Foo(0);
461 foo.<|>0;
462}
463"#,
464 );
465 }
466
467 #[test]
468 fn goto_def_for_ufcs_inherent_methods() {
469 check(
470 r#"
471struct Foo;
472impl Foo {
473 fn frobnicate() { }
474} //^^^^^^^^^^
475
476fn bar(foo: &Foo) {
477 Foo::frobnicate<|>();
478}
479"#,
480 );
481 }
482
483 #[test]
484 fn goto_def_for_ufcs_trait_methods_through_traits() {
485 check(
486 r#"
487trait Foo {
488 fn frobnicate();
489} //^^^^^^^^^^
490
491fn bar() {
492 Foo::frobnicate<|>();
493}
494"#,
495 );
496 }
497
498 #[test]
499 fn goto_def_for_ufcs_trait_methods_through_self() {
500 check(
501 r#"
502struct Foo;
503trait Trait {
504 fn frobnicate();
505} //^^^^^^^^^^
506impl Trait for Foo {}
507
508fn bar() {
509 Foo::frobnicate<|>();
510}
511"#,
512 );
513 }
514
515 #[test]
516 fn goto_definition_on_self() {
517 check(
518 r#"
519struct Foo;
520impl Foo {
521 //^^^
522 pub fn new() -> Self {
523 Self<|> {}
524 }
525}
526"#,
527 );
528 check(
529 r#"
530struct Foo;
531impl Foo {
532 //^^^
533 pub fn new() -> Self<|> {
534 Self {}
535 }
536}
537"#,
538 );
539
540 check(
541 r#"
542enum Foo { A }
543impl Foo {
544 //^^^
545 pub fn new() -> Self<|> {
546 Foo::A
547 }
548}
549"#,
550 );
551
552 check(
553 r#"
554enum Foo { A }
555impl Foo {
556 //^^^
557 pub fn thing(a: &Self<|>) {
558 }
559}
560"#,
561 );
562 }
563
564 #[test]
565 fn goto_definition_on_self_in_trait_impl() {
566 check(
567 r#"
568struct Foo;
569trait Make {
570 fn new() -> Self;
571}
572impl Make for Foo {
573 //^^^
574 fn new() -> Self {
575 Self<|> {}
576 }
577}
578"#,
579 );
580
581 check(
582 r#"
583struct Foo;
584trait Make {
585 fn new() -> Self;
586}
587impl Make for Foo {
588 //^^^
589 fn new() -> Self<|> {
590 Self {}
591 }
592}
593"#,
594 );
595 }
596
597 #[test]
598 fn goto_def_when_used_on_definition_name_itself() {
599 check(
600 r#"
601struct Foo<|> { value: u32 }
602 //^^^
603 "#,
604 );
605
606 check(
607 r#"
608struct Foo {
609 field<|>: string,
610} //^^^^^
611"#,
612 );
613
614 check(
615 r#"
616fn foo_test<|>() { }
617 //^^^^^^^^
618"#,
619 );
620
621 check(
622 r#"
623enum Foo<|> { Variant }
624 //^^^
625"#,
626 );
627
628 check(
629 r#"
630enum Foo {
631 Variant1,
632 Variant2<|>,
633 //^^^^^^^^
634 Variant3,
635}
636"#,
637 );
638
639 check(
640 r#"
641static INNER<|>: &str = "";
642 //^^^^^
643"#,
644 );
645
646 check(
647 r#"
648const INNER<|>: &str = "";
649 //^^^^^
650"#,
651 );
652
653 check(
654 r#"
655type Thing<|> = Option<()>;
656 //^^^^^
657"#,
658 );
659
660 check(
661 r#"
662trait Foo<|> { }
663 //^^^
664"#,
665 );
666
667 check(
668 r#"
669mod bar<|> { }
670 //^^^
671"#,
672 );
673 }
674
675 #[test]
676 fn goto_from_macro() {
677 check(
678 r#"
679macro_rules! id {
680 ($($tt:tt)*) => { $($tt)* }
681}
682fn foo() {}
683 //^^^
684id! {
685 fn bar() {
686 fo<|>o();
687 }
688}
689mod confuse_index { fn foo(); }
690"#,
691 );
692 }
693
694 #[test]
695 fn goto_through_format() {
696 check(
697 r#"
698#[macro_export]
699macro_rules! format {
700 ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*)))
701}
702#[rustc_builtin_macro]
703#[macro_export]
704macro_rules! format_args {
705 ($fmt:expr) => ({ /* compiler built-in */ });
706 ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
707}
708pub mod __export {
709 pub use crate::format_args;
710 fn foo() {} // for index confusion
711}
712fn foo() -> i8 {}
713 //^^^
714fn test() {
715 format!("{}", fo<|>o())
716}
717"#,
718 );
719 }
720
721 #[test]
722 fn goto_for_type_param() {
723 check(
724 r#"
725struct Foo<T: Clone> { t: <|>T }
726 //^
727"#,
728 );
729 }
730
731 #[test]
732 fn goto_within_macro() {
733 check(
734 r#"
735macro_rules! id {
736 ($($tt:tt)*) => ($($tt)*)
737}
738
739fn foo() {
740 let x = 1;
741 //^
742 id!({
743 let y = <|>x;
744 let z = y;
745 });
746}
747"#,
748 );
749
750 check(
751 r#"
752macro_rules! id {
753 ($($tt:tt)*) => ($($tt)*)
754}
755
756fn foo() {
757 let x = 1;
758 id!({
759 let y = x;
760 //^
761 let z = <|>y;
762 });
763}
764"#,
765 );
766 }
767
768 #[test]
769 fn goto_def_in_local_fn() {
770 check(
771 r#"
772fn main() {
773 fn foo() {
774 let x = 92;
775 //^
776 <|>x;
777 }
778}
779"#,
780 );
781 }
782
783 #[test]
784 fn goto_def_in_local_macro() {
785 check(
786 r#"
787fn bar() {
788 macro_rules! foo { () => { () } }
789 //^^^
790 <|>foo!();
791}
792"#,
793 );
794 }
795
796 #[test]
797 fn goto_def_for_field_init_shorthand() {
798 check(
799 r#"
800struct Foo { x: i32 }
801fn main() {
802 let x = 92;
803 //^
804 Foo { x<|> };
805}
806"#,
807 )
808 }
809
810 #[test]
811 fn goto_def_for_enum_variant_field() {
812 check(
813 r#"
814enum Foo {
815 Bar { x: i32 }
816} //^
817fn baz(foo: Foo) {
818 match foo {
819 Foo::Bar { x<|> } => x
820 };
821}
822"#,
823 );
824 }
825
826 #[test]
827 fn goto_def_for_enum_variant_self_pattern_const() {
828 check(
829 r#"
830enum Foo { Bar }
831 //^^^
832impl Foo {
833 fn baz(self) {
834 match self { Self::Bar<|> => {} }
835 }
836}
837"#,
838 );
839 }
840
841 #[test]
842 fn goto_def_for_enum_variant_self_pattern_record() {
843 check(
844 r#"
845enum Foo { Bar { val: i32 } }
846 //^^^
847impl Foo {
848 fn baz(self) -> i32 {
849 match self { Self::Bar<|> { val } => {} }
850 }
851}
852"#,
853 );
854 }
855
856 #[test]
857 fn goto_def_for_enum_variant_self_expr_const() {
858 check(
859 r#"
860enum Foo { Bar }
861 //^^^
862impl Foo {
863 fn baz(self) { Self::Bar<|>; }
864}
865"#,
866 );
867 }
868
869 #[test]
870 fn goto_def_for_enum_variant_self_expr_record() {
871 check(
872 r#"
873enum Foo { Bar { val: i32 } }
874 //^^^
875impl Foo {
876 fn baz(self) { Self::Bar<|> {val: 4}; }
877}
878"#,
879 );
880 }
881
882 #[test]
883 fn goto_def_for_type_alias_generic_parameter() {
884 check(
885 r#"
886type Alias<T> = T<|>;
887 //^
888"#,
889 )
890 }
891
892 #[test]
893 fn goto_def_for_macro_container() {
894 check(
895 r#"
896//- /lib.rs
897foo::module<|>::mac!();
898
899//- /foo/lib.rs
900pub mod module {
901 //^^^^^^
902 #[macro_export]
903 macro_rules! _mac { () => { () } }
904 pub use crate::_mac as mac;
905}
906"#,
907 );
908 }
909
910 #[test]
911 fn goto_def_for_assoc_ty_in_path() {
912 check(
913 r#"
914trait Iterator {
915 type Item;
916 //^^^^
917}
918
919fn f() -> impl Iterator<Item<|> = u8> {}
920"#,
921 );
922 }
923
924 #[test]
925 fn goto_def_for_assoc_ty_in_path_multiple() {
926 check(
927 r#"
928trait Iterator {
929 type A;
930 //^
931 type B;
932}
933
934fn f() -> impl Iterator<A<|> = u8, B = ()> {}
935"#,
936 );
937 check(
938 r#"
939trait Iterator {
940 type A;
941 type B;
942 //^
943}
944
945fn f() -> impl Iterator<A = u8, B<|> = ()> {}
946"#,
947 );
948 }
949
950 #[test]
951 fn goto_def_for_assoc_ty_ufcs() {
952 check(
953 r#"
954trait Iterator {
955 type Item;
956 //^^^^
957}
958
959fn g() -> <() as Iterator<Item<|> = ()>>::Item {}
960"#,
961 );
962 }
963
964 #[test]
965 fn goto_def_for_assoc_ty_ufcs_multiple() {
966 check(
967 r#"
968trait Iterator {
969 type A;
970 //^
971 type B;
972}
973
974fn g() -> <() as Iterator<A<|> = (), B = u8>>::B {}
975"#,
976 );
977 check(
978 r#"
979trait Iterator {
980 type A;
981 type B;
982 //^
983}
984
985fn g() -> <() as Iterator<A = (), B<|> = u8>>::A {}
986"#,
987 );
988 }
989}