1 use std::convert::TryInto;
3 use crate::{doc_links::token_as_doc_comment, FilePosition, NavigationTarget, RangeInfo, TryToNav};
4 use hir::{AsAssocItem, Semantics};
6 base_db::{AnchoredPath, FileId, FileLoader},
7 defs::{Definition, IdentClass},
8 helpers::pick_best_token,
11 use itertools::Itertools;
12 use syntax::{ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, T};
14 // Feature: Go to Definition
16 // Navigates to the definition of an identifier.
19 // | Editor | Shortcut
21 // | VS Code | kbd:[F12]
24 // image::https://user-images.githubusercontent.com/48062697/113065563-025fbe00-91b1-11eb-83e4-a5a703610b23.gif[]
25 pub(crate) fn goto_definition(
27 position: FilePosition,
28 ) -> Option<RangeInfo<Vec<NavigationTarget>>> {
29 let sema = &Semantics::new(db);
30 let file = sema.parse(position.file_id).syntax().clone();
32 pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
33 IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | COMMENT => 2,
34 kind if kind.is_trivia() => 0,
37 if let Some(doc_comment) = token_as_doc_comment(&original_token) {
38 return doc_comment.get_definition_with_descend_at(sema, position.offset, |def, _, _| {
39 let nav = def.try_to_nav(db)?;
40 Some(RangeInfo::new(original_token.text_range(), vec![nav]))
44 .descend_into_macros(original_token.clone())
47 let parent = token.parent()?;
48 if let Some(tt) = ast::TokenTree::cast(parent) {
49 if let Some(x) = try_lookup_include_path(sema, tt, token.clone(), position.file_id)
55 IdentClass::classify_token(sema, &token)?
59 try_find_trait_item_definition(sema.db, &def)
60 .unwrap_or_else(|| def_to_nav(sema.db, def))
67 .collect::<Vec<NavigationTarget>>();
69 Some(RangeInfo::new(original_token.text_range(), navs))
72 fn try_lookup_include_path(
73 sema: &Semantics<RootDatabase>,
77 ) -> Option<NavigationTarget> {
78 let token = ast::String::cast(token)?;
79 let path = token.value()?.into_owned();
80 let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
81 let name = macro_call.path()?.segment()?.name_ref()?;
82 if !matches!(&*name.text(), "include" | "include_str" | "include_bytes") {
85 let file_id = sema.db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?;
86 let size = sema.db.file_text(file_id).len().try_into().ok()?;
87 Some(NavigationTarget {
89 full_range: TextRange::new(0.into(), size),
99 /// finds the trait definition of an impl'd item
102 /// trait A { fn a(); }
104 /// impl A for S { fn a(); } // <-- on this function, will get the location of a() in the trait
106 fn try_find_trait_item_definition(
109 ) -> Option<Vec<NavigationTarget>> {
110 let name = def.name(db)?;
111 let assoc = def.as_assoc_item(db)?;
113 let imp = match assoc.container(db) {
114 hir::AssocItemContainer::Impl(imp) => imp,
118 let trait_ = imp.trait_(db)?;
122 .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten())
126 fn def_to_nav(db: &RootDatabase, def: Definition) -> Vec<NavigationTarget> {
127 def.try_to_nav(db).map(|it| vec![it]).unwrap_or_default()
132 use ide_db::base_db::FileRange;
133 use itertools::Itertools;
138 fn check(ra_fixture: &str) {
139 let (analysis, position, expected) = fixture::annotations(ra_fixture);
140 let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
142 panic!("unresolved reference")
145 let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start());
148 .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
150 .collect::<Vec<_>>();
151 let expected = expected
153 .map(|(FileRange { file_id, range }, _)| FileRange { file_id, range })
155 .collect::<Vec<_>>();
156 assert_eq!(expected, navs);
159 fn check_unresolved(ra_fixture: &str) {
160 let (analysis, position) = fixture::position(ra_fixture);
161 let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
163 assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {:?}", navs)
167 fn goto_def_in_mac_call_in_attr_invoc() {
170 //- proc_macros: identity
176 macro_rules! identity {
177 ($($tt:tt)*) => {$($tt)*};
180 #[proc_macros::identity]
182 identity!(Struct$0 { field: 0 });
190 fn goto_def_for_extern_crate() {
193 //- /main.rs crate:main deps:std
195 //- /std/lib.rs crate:std
203 fn goto_def_for_renamed_extern_crate() {
206 //- /main.rs crate:main deps:std
207 extern crate std as abc$0;
208 //- /std/lib.rs crate:std
216 fn goto_def_in_items() {
227 fn goto_def_at_start_of_item() {
238 fn goto_definition_resolves_correct_name() {
257 fn goto_def_for_module_declaration() {
282 fn goto_def_for_macros() {
285 macro_rules! foo { () => { () } }
295 fn goto_def_for_macros_from_other_crates() {
298 //- /lib.rs crate:main deps:foo
304 //- /foo/lib.rs crate:foo
306 macro_rules! foo { () => { () } }
313 fn goto_def_for_macros_in_use_tree() {
316 //- /lib.rs crate:main deps:foo
319 //- /foo/lib.rs crate:foo
321 macro_rules! foo { () => { () } }
328 fn goto_def_for_macro_defined_fn_with_arg() {
332 macro_rules! define_fn {
333 ($name:ident) => (fn $name() {})
347 fn goto_def_for_macro_defined_fn_no_arg() {
351 macro_rules! define_fn {
366 fn goto_definition_works_for_macro_inside_pattern() {
370 macro_rules! foo {() => {0}}
383 fn goto_definition_works_for_macro_inside_match_arm_lhs() {
387 macro_rules! foo {() => {0}}
399 fn goto_def_for_use_alias() {
402 //- /lib.rs crate:main deps:foo
405 //- /foo/lib.rs crate:foo
413 fn goto_def_for_use_alias_foo_macro() {
416 //- /lib.rs crate:main deps:foo
417 use foo::foo as bar$0;
419 //- /foo/lib.rs crate:foo
421 macro_rules! foo { () => { () } }
428 fn goto_def_for_methods() {
433 fn frobnicate(&self) { }
445 fn goto_def_for_fields() {
460 fn goto_def_for_record_fields() {
478 fn goto_def_for_record_pat_fields() {
486 fn bar(foo: Foo) -> Foo {
487 let Foo { spam$0: _, } = foo
494 fn goto_def_for_record_fields_macros() {
497 macro_rules! m { () => { 92 };}
498 struct Foo { spam: u32 }
509 fn goto_for_tuple_fields() {
524 fn goto_def_for_ufcs_inherent_methods() {
540 fn goto_def_for_ufcs_trait_methods_through_traits() {
555 fn goto_def_for_ufcs_trait_methods_through_self() {
562 impl Trait for Foo {}
572 fn goto_definition_on_self() {
578 pub fn new() -> Self {
589 pub fn new() -> Self$0 {
601 pub fn new() -> Self$0 {
613 pub fn thing(a: &Self$0) {
621 fn goto_definition_on_self_in_trait_impl() {
654 fn goto_def_when_used_on_definition_name_itself() {
657 struct Foo$0 { value: u32 }
679 enum Foo$0 { Variant }
697 static INNER$0: &str = "";
704 const INNER$0: &str = "";
711 type Thing$0 = Option<()>;
732 fn goto_from_macro() {
736 ($($tt:tt)*) => { $($tt)* }
745 mod confuse_index { fn foo(); }
751 fn goto_through_format() {
755 macro_rules! format {
756 ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*)))
758 #[rustc_builtin_macro]
760 macro_rules! format_args {
761 ($fmt:expr) => ({ /* compiler built-in */ });
762 ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
765 pub use crate::format_args;
766 fn foo() {} // for index confusion
771 format!("{}", fo$0o())
778 fn goto_through_included_file() {
782 #[rustc_builtin_macro]
783 macro_rules! include {}
786 //^^^^^^^^^^^^^^^^^^^
803 fn goto_for_type_param() {
806 struct Foo<T: Clone> { t: $0T }
813 fn goto_within_macro() {
817 ($($tt:tt)*) => ($($tt)*)
834 ($($tt:tt)*) => ($($tt)*)
850 fn goto_def_in_local_fn() {
865 fn goto_def_in_local_macro() {
869 macro_rules! foo { () => { () } }
878 fn goto_def_for_field_init_shorthand() {
881 struct Foo { x: i32 }
893 fn goto_def_for_enum_variant_field() {
902 Foo::Bar { x$0 } => x
911 fn goto_def_for_enum_variant_self_pattern_const() {
918 match self { Self::Bar$0 => {} }
926 fn goto_def_for_enum_variant_self_pattern_record() {
929 enum Foo { Bar { val: i32 } }
932 fn baz(self) -> i32 {
933 match self { Self::Bar$0 { val } => {} }
941 fn goto_def_for_enum_variant_self_expr_const() {
947 fn baz(self) { Self::Bar$0; }
954 fn goto_def_for_enum_variant_self_expr_record() {
957 enum Foo { Bar { val: i32 } }
960 fn baz(self) { Self::Bar$0 {val: 4}; }
967 fn goto_def_for_type_alias_generic_parameter() {
977 fn goto_def_for_macro_container() {
980 //- /lib.rs crate:main deps:foo
981 foo::module$0::mac!();
983 //- /foo/lib.rs crate:foo
987 macro_rules! _mac { () => { () } }
988 pub use crate::_mac as mac;
995 fn goto_def_for_assoc_ty_in_path() {
1003 fn f() -> impl Iterator<Item$0 = u8> {}
1009 fn unknown_assoc_ty() {
1012 trait Iterator { type Item; }
1013 fn f() -> impl Iterator<Invalid$0 = u8> {}
1019 fn goto_def_for_assoc_ty_in_path_multiple() {
1028 fn f() -> impl Iterator<A$0 = u8, B = ()> {}
1039 fn f() -> impl Iterator<A = u8, B$0 = ()> {}
1045 fn goto_def_for_assoc_ty_ufcs() {
1053 fn g() -> <() as Iterator<Item$0 = ()>>::Item {}
1059 fn goto_def_for_assoc_ty_ufcs_multiple() {
1068 fn g() -> <() as Iterator<A$0 = (), B = u8>>::B {}
1079 fn g() -> <() as Iterator<A = (), B$0 = u8>>::A {}
1085 fn goto_self_param_ty_specified() {
1091 fn bar(self: &Foo) {
1100 fn goto_self_param_on_decl() {
1114 fn goto_lifetime_param_on_decl() {
1117 fn foo<'foobar$0>(_: &'foobar ()) {
1124 fn goto_lifetime_param_decl() {
1127 fn foo<'foobar>(_: &'foobar$0 ()) {
1134 fn goto_lifetime_param_decl_nested() {
1137 fn foo<'foobar>(_: &'foobar ()) {
1138 fn foo<'foobar>(_: &'foobar$0 ()) {}
1145 fn goto_lifetime_hrtb() {
1146 // FIXME: requires the HIR to somehow track these hrtb lifetimes
1150 fn foo<T>() where for<'a> T: Foo<&'a$0 (u8, u16)>, {}
1157 fn foo<T>() where for<'a$0> T: Foo<&'a (u8, u16)>, {}
1164 fn goto_lifetime_hrtb_for_type() {
1165 // FIXME: requires ForTypes to be implemented
1168 fn foo<T>() where T: for<'a> Foo<&'a$0 (u8, u16)>, {}
1178 fn foo<'foo>(_: &'foo ()) {
1190 fn goto_def_for_intra_doc_link_same_file() {
1193 /// Blah, [`bar`](bar) .. [`foo`](foo$0) has [`bar`](bar)
1196 /// You might want to see [`std::fs::read()`] too.
1205 fn goto_def_for_intra_doc_link_inner() {
1220 fn goto_incomplete_field() {
1225 fn foo() { A { a$0: }; }
1231 fn goto_proc_macro() {
1234 //- /main.rs crate:main deps:mac
1239 //- /mac.rs crate:mac
1248 fn goto_intra_doc_links() {
1253 /// This is the item. Cool!
1258 /// Gives you a [`TheItem$0`].
1260 /// [`TheItem`]: theitem::TheItem
1261 pub fn gimme() -> theitem::TheItem {
1269 fn goto_ident_from_pat_macro() {
1273 ($name:ident) => { Enum::Variant1($name) }
1287 Enum::Variant2 => {}
1300 let str = include_str!("foo.txt$0");
1310 fn goto_def_of_trait_impl_fn() {
1320 impl Twait for Stwuct {
1328 fn goto_def_of_trait_impl_const() {
1338 impl Twait for Stwuct {
1339 const NOMS$0: bool = true;
1346 fn goto_def_of_trait_impl_type_alias() {
1356 impl Twait for Stwuct {
1364 fn goto_def_derive_input() {
1368 #[rustc_builtin_macro]
1378 #[rustc_builtin_macro]
1381 #[cfg_attr(feature = "false", derive)]
1390 #[rustc_builtin_macro]
1394 #[derive(foo::Copy$0)]
1403 #[rustc_builtin_macro]
1406 #[derive(foo$0::Copy)]
1413 fn goto_def_in_macro_multi() {
1422 fn $ident(Foo { $ident }: Foo) {}
1439 let _: $ident = $ident;