1 use std
::mem
::discriminant
;
4 doc_links
::token_as_doc_comment
, navigation_target
::ToNav
, FilePosition
, NavigationTarget
,
7 use hir
::{AsAssocItem, AssocItem, Semantics}
;
9 base_db
::{AnchoredPath, FileId, FileLoader}
,
10 defs
::{Definition, IdentClass}
,
11 helpers
::pick_best_token
,
14 use itertools
::Itertools
;
15 use syntax
::{ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, T}
;
17 // Feature: Go to Definition
19 // Navigates to the definition of an identifier.
21 // For outline modules, this will navigate to the source file of the module.
24 // | Editor | Shortcut
26 // | VS Code | kbd:[F12]
29 // image::https://user-images.githubusercontent.com/48062697/113065563-025fbe00-91b1-11eb-83e4-a5a703610b23.gif[]
30 pub(crate) fn goto_definition(
32 position
: FilePosition
,
33 ) -> Option
<RangeInfo
<Vec
<NavigationTarget
>>> {
34 let sema
= &Semantics
::new(db
);
35 let file
= sema
.parse(position
.file_id
).syntax().clone();
37 pick_best_token(file
.token_at_offset(position
.offset
), |kind
| match kind
{
46 // index and prefix ops
47 T
!['
['
] | T
!['
]'
] | T
![?
] | T
![*] | T
![-] | T
![!] => 3,
48 kind
if kind
.is_keyword() => 2,
49 T
!['
('
] | T
!['
)'
] => 2,
50 kind
if kind
.is_trivia() => 0,
53 if let Some(doc_comment
) = token_as_doc_comment(&original_token
) {
54 return doc_comment
.get_definition_with_descend_at(
57 |def
, _
, link_range
| {
58 let nav
= def
.try_to_nav(db
)?
;
59 Some(RangeInfo
::new(link_range
, vec
![nav
]))
64 .descend_into_macros(original_token
.clone())
67 let parent
= token
.parent()?
;
68 if let Some(tt
) = ast
::TokenTree
::cast(parent
) {
69 if let Some(x
) = try_lookup_include_path(sema
, tt
, token
.clone(), position
.file_id
)
75 IdentClass
::classify_token(sema
, &token
)?
79 if let Definition
::ExternCrateDecl(crate_def
) = def
{
82 .map(|it
| it
.root_module().to_nav(sema
.db
))
86 try_filter_trait_item_definition(sema
, &def
)
87 .unwrap_or_else(|| def_to_nav(sema
.db
, def
))
94 .collect
::<Vec
<NavigationTarget
>>();
96 Some(RangeInfo
::new(original_token
.text_range(), navs
))
99 fn try_lookup_include_path(
100 sema
: &Semantics
<'_
, RootDatabase
>,
104 ) -> Option
<NavigationTarget
> {
105 let token
= ast
::String
::cast(token
)?
;
106 let path
= token
.value()?
.into_owned();
107 let macro_call
= tt
.syntax().parent().and_then(ast
::MacroCall
::cast
)?
;
108 let name
= macro_call
.path()?
.segment()?
.name_ref()?
;
109 if !matches
!(&*name
.text(), "include" | "include_str" | "include_bytes") {
113 // Ignore non-built-in macros to account for shadowing
114 if let Some(it
) = sema
.resolve_macro_call(¯o_call
) {
115 if !matches
!(it
.kind(sema
.db
), hir
::MacroKind
::BuiltIn
) {
120 let file_id
= sema
.db
.resolve_path(AnchoredPath { anchor: file_id, path: &path }
)?
;
121 let size
= sema
.db
.file_text(file_id
).len().try_into().ok()?
;
122 Some(NavigationTarget
{
124 full_range
: TextRange
::new(0.into
(), size
),
129 container_name
: None
,
134 /// finds the trait definition of an impl'd item, except function
137 /// trait A { type a; }
139 /// impl A for S { type a = i32; } // <-- on this associate type, will get the location of a in the trait
141 fn try_filter_trait_item_definition(
142 sema
: &Semantics
<'_
, RootDatabase
>,
144 ) -> Option
<Vec
<NavigationTarget
>> {
146 let assoc
= def
.as_assoc_item(db
)?
;
148 AssocItem
::Function(..) => None
,
149 AssocItem
::Const(..) | AssocItem
::TypeAlias(..) => {
150 let imp
= match assoc
.container(db
) {
151 hir
::AssocItemContainer
::Impl(imp
) => imp
,
154 let trait_
= imp
.trait_(db
)?
;
155 let name
= def
.name(db
)?
;
156 let discri_value
= discriminant(&assoc
);
160 .filter(|itm
| discriminant(*itm
) == discri_value
)
161 .find_map(|itm
| (itm
.name(db
)?
== name
).then(|| itm
.try_to_nav(db
)).flatten())
167 fn def_to_nav(db
: &RootDatabase
, def
: Definition
) -> Vec
<NavigationTarget
> {
168 def
.try_to_nav(db
).map(|it
| vec
![it
]).unwrap_or_default()
173 use ide_db
::base_db
::FileRange
;
174 use itertools
::Itertools
;
179 fn check(ra_fixture
: &str) {
180 let (analysis
, position
, expected
) = fixture
::annotations(ra_fixture
);
181 let navs
= analysis
.goto_definition(position
).unwrap().expect("no definition found").info
;
183 let cmp
= |&FileRange { file_id, range }
: &_
| (file_id
, range
.start());
186 .map(|nav
| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() }
)
188 .collect
::<Vec
<_
>>();
189 let expected
= expected
191 .map(|(FileRange { file_id, range }
, _
)| FileRange { file_id, range }
)
193 .collect
::<Vec
<_
>>();
194 assert_eq
!(expected
, navs
);
197 fn check_unresolved(ra_fixture
: &str) {
198 let (analysis
, position
) = fixture
::position(ra_fixture
);
199 let navs
= analysis
.goto_definition(position
).unwrap().expect("no definition found").info
;
201 assert
!(navs
.is_empty(), "didn't expect this to resolve anywhere: {navs:?}")
205 fn goto_def_if_items_same_name() {
222 fn goto_def_in_mac_call_in_attr_invoc() {
225 //- proc_macros: identity
231 macro_rules! identity {
232 ($($tt:tt)*) => {$($tt)*};
235 #[proc_macros::identity]
237 identity!(Struct$0 { field: 0 });
245 fn goto_def_for_extern_crate() {
248 //- /main.rs crate:main deps:std
250 //- /std/lib.rs crate:std
258 fn goto_def_for_renamed_extern_crate() {
261 //- /main.rs crate:main deps:std
262 extern crate std as abc$0;
263 //- /std/lib.rs crate:std
271 fn goto_def_in_items() {
282 fn goto_def_at_start_of_item() {
293 fn goto_definition_resolves_correct_name() {
312 fn goto_def_for_module_declaration() {
337 fn goto_def_for_macros() {
340 macro_rules! foo { () => { () } }
350 fn goto_def_for_macros_from_other_crates() {
353 //- /lib.rs crate:main deps:foo
359 //- /foo/lib.rs crate:foo
361 macro_rules! foo { () => { () } }
368 fn goto_def_for_macros_in_use_tree() {
371 //- /lib.rs crate:main deps:foo
374 //- /foo/lib.rs crate:foo
376 macro_rules! foo { () => { () } }
383 fn goto_def_for_macro_defined_fn_with_arg() {
387 macro_rules! define_fn {
388 ($name:ident) => (fn $name() {})
402 fn goto_def_for_macro_defined_fn_no_arg() {
406 macro_rules! define_fn {
421 fn goto_definition_works_for_macro_inside_pattern() {
425 macro_rules! foo {() => {0}}
438 fn goto_definition_works_for_macro_inside_match_arm_lhs() {
442 macro_rules! foo {() => {0}}
454 fn goto_def_for_use_alias() {
457 //- /lib.rs crate:main deps:foo
460 //- /foo/lib.rs crate:foo
468 fn goto_def_for_use_alias_foo_macro() {
471 //- /lib.rs crate:main deps:foo
472 use foo::foo as bar$0;
474 //- /foo/lib.rs crate:foo
476 macro_rules! foo { () => { () } }
483 fn goto_def_for_methods() {
488 fn frobnicate(&self) { }
500 fn goto_def_for_fields() {
515 fn goto_def_for_record_fields() {
533 fn goto_def_for_record_pat_fields() {
541 fn bar(foo: Foo) -> Foo {
542 let Foo { spam$0: _, } = foo
549 fn goto_def_for_record_fields_macros() {
552 macro_rules! m { () => { 92 };}
553 struct Foo { spam: u32 }
564 fn goto_for_tuple_fields() {
579 fn goto_def_for_ufcs_inherent_methods() {
595 fn goto_def_for_ufcs_trait_methods_through_traits() {
610 fn goto_def_for_ufcs_trait_methods_through_self() {
617 impl Trait for Foo {}
627 fn goto_definition_on_self() {
633 pub fn new() -> Self {
644 pub fn new() -> Self$0 {
656 pub fn new() -> Self$0 {
668 pub fn thing(a: &Self$0) {
676 fn goto_definition_on_self_in_trait_impl() {
709 fn goto_def_when_used_on_definition_name_itself() {
712 struct Foo$0 { value: u32 }
734 enum Foo$0 { Variant }
752 static INNER$0: &str = "";
759 const INNER$0: &str = "";
766 type Thing$0 = Option<()>;
794 fn goto_from_macro() {
798 ($($tt:tt)*) => { $($tt)* }
807 mod confuse_index { fn foo(); }
813 fn goto_through_format() {
817 macro_rules! format {
818 ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*)))
820 #[rustc_builtin_macro]
822 macro_rules! format_args {
823 ($fmt:expr) => ({ /* compiler built-in */ });
824 ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
827 pub use crate::format_args;
828 fn foo() {} // for index confusion
833 format!("{}", fo$0o())
840 fn goto_through_included_file() {
844 #[rustc_builtin_macro]
845 macro_rules! include {}
865 fn goto_through_included_file_struct_with_doc_comment() {
869 #[rustc_builtin_macro]
870 macro_rules! include {}
883 /// This is a doc comment
891 fn goto_for_type_param() {
894 struct Foo<T: Clone> { t: $0T }
901 fn goto_within_macro() {
905 ($($tt:tt)*) => ($($tt)*)
922 ($($tt:tt)*) => ($($tt)*)
938 fn goto_def_in_local_fn() {
953 fn goto_def_in_local_macro() {
957 macro_rules! foo { () => { () } }
966 fn goto_def_for_field_init_shorthand() {
969 struct Foo { x: i32 }
981 fn goto_def_for_enum_variant_field() {
990 Foo::Bar { x$0 } => x
999 fn goto_def_for_enum_variant_self_pattern_const() {
1006 match self { Self::Bar$0 => {} }
1014 fn goto_def_for_enum_variant_self_pattern_record() {
1017 enum Foo { Bar { val: i32 } }
1020 fn baz(self) -> i32 {
1021 match self { Self::Bar$0 { val } => {} }
1029 fn goto_def_for_enum_variant_self_expr_const() {
1035 fn baz(self) { Self::Bar$0; }
1042 fn goto_def_for_enum_variant_self_expr_record() {
1045 enum Foo { Bar { val: i32 } }
1048 fn baz(self) { Self::Bar$0 {val: 4}; }
1055 fn goto_def_for_type_alias_generic_parameter() {
1058 type Alias<T> = T$0;
1065 fn goto_def_for_macro_container() {
1068 //- /lib.rs crate:main deps:foo
1069 foo::module$0::mac!();
1071 //- /foo/lib.rs crate:foo
1075 macro_rules! _mac { () => { () } }
1076 pub use crate::_mac as mac;
1083 fn goto_def_for_assoc_ty_in_path() {
1091 fn f() -> impl Iterator<Item$0 = u8> {}
1097 fn goto_def_for_super_assoc_ty_in_path() {
1107 fn f() -> impl Sub<Item$0 = u8> {}
1113 fn goto_def_for_module_declaration_in_path_if_types_and_values_same_name() {
1123 let _foo_enum: bar::Foo$0 = bar::Foo {};
1130 fn unknown_assoc_ty() {
1133 trait Iterator { type Item; }
1134 fn f() -> impl Iterator<Invalid$0 = u8> {}
1140 fn goto_def_for_assoc_ty_in_path_multiple() {
1149 fn f() -> impl Iterator<A$0 = u8, B = ()> {}
1160 fn f() -> impl Iterator<A = u8, B$0 = ()> {}
1166 fn goto_def_for_assoc_ty_ufcs() {
1174 fn g() -> <() as Iterator<Item$0 = ()>>::Item {}
1180 fn goto_def_for_assoc_ty_ufcs_multiple() {
1189 fn g() -> <() as Iterator<A$0 = (), B = u8>>::B {}
1200 fn g() -> <() as Iterator<A = (), B$0 = u8>>::A {}
1206 fn goto_self_param_ty_specified() {
1212 fn bar(self: &Foo) {
1221 fn goto_self_param_on_decl() {
1235 fn goto_lifetime_param_on_decl() {
1238 fn foo<'foobar$0>(_: &'foobar ()) {
1245 fn goto_lifetime_param_decl() {
1248 fn foo<'foobar>(_: &'foobar$0 ()) {
1255 fn goto_lifetime_param_decl_nested() {
1258 fn foo<'foobar>(_: &'foobar ()) {
1259 fn foo<'foobar>(_: &'foobar$0 ()) {}
1266 fn goto_lifetime_hrtb() {
1267 // FIXME: requires the HIR to somehow track these hrtb lifetimes
1271 fn foo<T>() where for<'a> T: Foo<&'a$0 (u8, u16)>, {}
1278 fn foo<T>() where for<'a$0> T: Foo<&'a (u8, u16)>, {}
1285 fn goto_lifetime_hrtb_for_type() {
1286 // FIXME: requires ForTypes to be implemented
1289 fn foo<T>() where T: for<'a> Foo<&'a$0 (u8, u16)>, {}
1299 fn foo<'foo>(_: &'foo ()) {
1311 fn goto_def_for_intra_doc_link_same_file() {
1314 /// Blah, [`bar`](bar) .. [`foo`](foo$0) has [`bar`](bar)
1317 /// You might want to see [`std::fs::read()`] too.
1326 fn goto_def_for_intra_doc_link_inner() {
1341 fn goto_incomplete_field() {
1346 fn foo() { A { a$0: }; }
1352 fn goto_proc_macro() {
1355 //- /main.rs crate:main deps:mac
1360 //- /mac.rs crate:mac
1361 #![crate_type="proc-macro"]
1370 fn goto_intra_doc_links() {
1375 /// This is the item. Cool!
1380 /// Gives you a [`TheItem$0`].
1382 /// [`TheItem`]: theitem::TheItem
1383 pub fn gimme() -> theitem::TheItem {
1391 fn goto_ident_from_pat_macro() {
1395 ($name:ident) => { Enum::Variant1($name) }
1409 Enum::Variant2 => {}
1422 #[rustc_builtin_macro]
1423 macro_rules! include_str {}
1426 let str = include_str!("foo.txt$0");
1436 fn goto_doc_include_str() {
1440 #[rustc_builtin_macro]
1441 macro_rules! include_str {}
1443 #[doc = include_str!("docs.md$0")]
1454 fn goto_shadow_include() {
1458 macro_rules! include {
1459 ("included.rs") => {}
1462 include!("included.rs$0");
1470 mod goto_impl_of_trait_fn
{
1473 fn cursor_on_impl() {
1482 impl Twait for Stwuct {
1499 impl Twait for Stwuct {
1511 fn method_call_inside_block() {
1521 impl Twait for Stwuct {
1543 impl Twait for Stwuct {
1555 fn where_clause_can_work() {
1564 impl <T:EA> G for Gen<T> {
1568 impl <T> G for Gen<T>
1578 let gen = Gen::<A>(A);
1585 fn wc_case_is_ok() {
1592 trait Bound: BParent{}
1594 impl <T> G for Gen<T>
1604 let gen = Gen::<A>(A);
1612 fn method_call_defaulted() {
1622 impl Twait for Stwuct {
1633 fn method_call_on_generic() {
1641 fn f<T: Twait>(s: T) {
1650 fn goto_def_of_trait_impl_const() {
1660 impl Twait for Stwuct {
1661 const NOMS$0: bool = true;
1668 fn goto_def_of_trait_impl_type_alias() {
1678 impl Twait for Stwuct {
1686 fn goto_def_derive_input() {
1690 #[rustc_builtin_macro]
1700 #[rustc_builtin_macro]
1703 #[cfg_attr(feature = "false", derive)]
1712 #[rustc_builtin_macro]
1716 #[derive(foo::Copy$0)]
1725 #[rustc_builtin_macro]
1728 #[derive(foo$0::Copy)]
1735 fn goto_def_in_macro_multi() {
1744 fn $ident(Foo { $ident }: Foo) {}
1761 let _: $ident = $ident;
1772 fn goto_await_poll() {
1775 //- minicore: future
1779 impl core::future::Future for MyFut {
1784 self: std::pin::Pin<&mut Self>,
1785 cx: &mut std::task::Context<'_>
1786 ) -> std::task::Poll<Self::Output>
1800 fn goto_await_into_future_poll() {
1803 //- minicore: future
1807 impl core::future::IntoFuture for Futurable {
1808 type IntoFuture = MyFut;
1813 impl core::future::Future for MyFut {
1818 self: std::pin::Pin<&mut Self>,
1819 cx: &mut std::task::Context<'_>
1820 ) -> std::task::Poll<Self::Output>
1841 impl core::ops::Try for Struct {
1856 fn goto_index_op() {
1863 impl core::ops::Index<usize> for Struct {
1878 fn goto_prefix_op() {
1885 impl core::ops::Deref for Struct {
1907 impl core::ops::Add for Struct {
1922 fn goto_bin_op_multiple_impl() {
1927 impl core::ops::Add for S {
1932 impl core::ops::Add<usize> for S {
1947 impl core::ops::Add for S {
1951 impl core::ops::Add<usize> for S {
1965 fn path_call_multiple_trait_impl() {
1971 impl Trait<i32> for usize {
1975 impl Trait<i64> for usize {
1989 impl Trait<i32> for usize {
1992 impl Trait<i64> for usize {
2004 fn query_impls_in_nearest_block() {