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