1 use std::convert::TryInto;
4 use hir::{AsAssocItem, InFile, ModuleDef, Semantics};
6 base_db::{AnchoredPath, FileId, FileLoader},
7 defs::{Definition, NameClass, NameRefClass},
8 helpers::pick_best_token,
11 use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, T};
15 doc_links::{doc_attributes, extract_definitions_from_markdown, resolve_doc_path_for_def},
16 FilePosition, NavigationTarget, RangeInfo,
19 // Feature: Go to Definition
21 // Navigates to the definition of an identifier.
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 {
38 IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | COMMENT => 2,
39 kind if kind.is_trivia() => 0,
42 let token = sema.descend_into_macros(original_token.clone());
43 let parent = token.parent()?;
44 if let Some(_) = ast::Comment::cast(token.clone()) {
45 let (attributes, def) = doc_attributes(&sema, &parent)?;
47 let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?;
49 extract_definitions_from_markdown(docs.as_str()).into_iter().find(|(range, ..)| {
50 doc_mapping.map(*range).map_or(false, |InFile { file_id, value: range }| {
51 file_id == position.file_id.into() && range.contains(position.offset)
54 let nav = resolve_doc_path_for_def(db, def, &link, ns)?.try_to_nav(db)?;
55 return Some(RangeInfo::new(original_token.text_range(), vec![nav]));
57 let nav = match_ast! {
59 ast::NameRef(name_ref) => {
60 reference_definition(&sema, Either::Right(&name_ref))
63 let def = NameClass::classify(&sema, &name)?.referenced_or_defined();
64 try_find_trait_item_definition(sema.db, &def)
65 .or_else(|| def.try_to_nav(sema.db))
67 ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, <) {
68 let def = name_class.referenced_or_defined();
69 def.try_to_nav(sema.db)
71 reference_definition(&sema, Either::Left(<))
73 ast::TokenTree(tt) => try_lookup_include_path(sema.db, tt, token, position.file_id),
78 Some(RangeInfo::new(original_token.text_range(), nav.into_iter().collect()))
81 fn try_lookup_include_path(
86 ) -> Option<NavigationTarget> {
87 let path = ast::String::cast(token)?.value()?.into_owned();
88 let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
89 let name = macro_call.path()?.segment()?.name_ref()?;
90 if !matches!(&*name.text(), "include" | "include_str" | "include_bytes") {
93 let file_id = db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?;
94 let size = db.file_text(file_id).len().try_into().ok()?;
95 Some(NavigationTarget {
97 full_range: TextRange::new(0.into(), size),
101 container_name: None,
107 /// finds the trait definition of an impl'd item
110 /// trait A { fn a(); }
112 /// impl A for S { fn a(); } // <-- on this function, will get the location of a() in the trait
114 fn try_find_trait_item_definition(db: &RootDatabase, def: &Definition) -> Option<NavigationTarget> {
115 let name = def.name(db)?;
116 let assoc = match def {
117 Definition::ModuleDef(ModuleDef::Function(f)) => f.as_assoc_item(db),
118 Definition::ModuleDef(ModuleDef::Const(c)) => c.as_assoc_item(db),
119 Definition::ModuleDef(ModuleDef::TypeAlias(ty)) => ty.as_assoc_item(db),
123 let imp = match assoc.container(db) {
124 hir::AssocItemContainer::Impl(imp) => imp,
128 let trait_ = imp.trait_(db)?;
132 .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten())
135 pub(crate) fn reference_definition(
136 sema: &Semantics<RootDatabase>,
137 name_ref: Either<&ast::Lifetime, &ast::NameRef>,
138 ) -> Option<NavigationTarget> {
139 let name_kind = name_ref.either(
140 |lifetime| NameRefClass::classify_lifetime(sema, lifetime),
141 |name_ref| NameRefClass::classify(sema, name_ref),
143 let def = name_kind.referenced();
144 def.try_to_nav(sema.db)
149 use ide_db::base_db::FileRange;
153 fn check(ra_fixture: &str) {
154 let (analysis, position, expected) = fixture::nav_target_annotation(ra_fixture);
156 analysis.goto_definition(position).unwrap().expect("no definition found").info;
158 panic!("unresolved reference")
160 assert_eq!(navs.len(), 1);
162 let nav = navs.pop().unwrap();
163 assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() });
166 fn check_unresolved(ra_fixture: &str) {
167 let (analysis, position) = fixture::position(ra_fixture);
168 let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
170 assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {:?}", navs)
174 fn goto_def_for_extern_crate() {
177 //- /main.rs crate:main deps:std
179 //- /std/lib.rs crate:std
187 fn goto_def_for_renamed_extern_crate() {
190 //- /main.rs crate:main deps:std
191 extern crate std as abc$0;
192 //- /std/lib.rs crate:std
200 fn goto_def_in_items() {
211 fn goto_def_at_start_of_item() {
222 fn goto_definition_resolves_correct_name() {
241 fn goto_def_for_module_declaration() {
266 fn goto_def_for_macros() {
269 macro_rules! foo { () => { () } }
279 fn goto_def_for_macros_from_other_crates() {
282 //- /lib.rs crate:main deps:foo
288 //- /foo/lib.rs crate:foo
290 macro_rules! foo { () => { () } }
297 fn goto_def_for_macros_in_use_tree() {
300 //- /lib.rs crate:main deps:foo
303 //- /foo/lib.rs crate:foo
305 macro_rules! foo { () => { () } }
312 fn goto_def_for_macro_defined_fn_with_arg() {
316 macro_rules! define_fn {
317 ($name:ident) => (fn $name() {})
331 fn goto_def_for_macro_defined_fn_no_arg() {
335 macro_rules! define_fn {
350 fn goto_definition_works_for_macro_inside_pattern() {
354 macro_rules! foo {() => {0}}
367 fn goto_definition_works_for_macro_inside_match_arm_lhs() {
371 macro_rules! foo {() => {0}}
383 fn goto_def_for_use_alias() {
386 //- /lib.rs crate:main deps:foo
389 //- /foo/lib.rs crate:foo
397 fn goto_def_for_use_alias_foo_macro() {
400 //- /lib.rs crate:main deps:foo
401 use foo::foo as bar$0;
403 //- /foo/lib.rs crate:foo
405 macro_rules! foo { () => { () } }
412 fn goto_def_for_methods() {
417 fn frobnicate(&self) { }
429 fn goto_def_for_fields() {
444 fn goto_def_for_record_fields() {
462 fn goto_def_for_record_pat_fields() {
470 fn bar(foo: Foo) -> Foo {
471 let Foo { spam$0: _, } = foo
478 fn goto_def_for_record_fields_macros() {
481 macro_rules! m { () => { 92 };}
482 struct Foo { spam: u32 }
493 fn goto_for_tuple_fields() {
508 fn goto_def_for_ufcs_inherent_methods() {
524 fn goto_def_for_ufcs_trait_methods_through_traits() {
539 fn goto_def_for_ufcs_trait_methods_through_self() {
546 impl Trait for Foo {}
556 fn goto_definition_on_self() {
562 pub fn new() -> Self {
573 pub fn new() -> Self$0 {
585 pub fn new() -> Self$0 {
597 pub fn thing(a: &Self$0) {
605 fn goto_definition_on_self_in_trait_impl() {
638 fn goto_def_when_used_on_definition_name_itself() {
641 struct Foo$0 { value: u32 }
663 enum Foo$0 { Variant }
681 static INNER$0: &str = "";
688 const INNER$0: &str = "";
695 type Thing$0 = Option<()>;
716 fn goto_from_macro() {
720 ($($tt:tt)*) => { $($tt)* }
729 mod confuse_index { fn foo(); }
735 fn goto_through_format() {
739 macro_rules! format {
740 ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*)))
742 #[rustc_builtin_macro]
744 macro_rules! format_args {
745 ($fmt:expr) => ({ /* compiler built-in */ });
746 ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
749 pub use crate::format_args;
750 fn foo() {} // for index confusion
755 format!("{}", fo$0o())
762 fn goto_through_included_file() {
766 #[rustc_builtin_macro]
767 macro_rules! include {}
770 //^^^^^^^^^^^^^^^^^^^
787 fn goto_for_type_param() {
790 struct Foo<T: Clone> { t: $0T }
797 fn goto_within_macro() {
801 ($($tt:tt)*) => ($($tt)*)
818 ($($tt:tt)*) => ($($tt)*)
834 fn goto_def_in_local_fn() {
849 fn goto_def_in_local_macro() {
853 macro_rules! foo { () => { () } }
862 fn goto_def_for_field_init_shorthand() {
865 struct Foo { x: i32 }
876 fn goto_def_for_enum_variant_field() {
884 Foo::Bar { x$0 } => x
892 fn goto_def_for_enum_variant_self_pattern_const() {
899 match self { Self::Bar$0 => {} }
907 fn goto_def_for_enum_variant_self_pattern_record() {
910 enum Foo { Bar { val: i32 } }
913 fn baz(self) -> i32 {
914 match self { Self::Bar$0 { val } => {} }
922 fn goto_def_for_enum_variant_self_expr_const() {
928 fn baz(self) { Self::Bar$0; }
935 fn goto_def_for_enum_variant_self_expr_record() {
938 enum Foo { Bar { val: i32 } }
941 fn baz(self) { Self::Bar$0 {val: 4}; }
948 fn goto_def_for_type_alias_generic_parameter() {
958 fn goto_def_for_macro_container() {
961 //- /lib.rs crate:main deps:foo
962 foo::module$0::mac!();
964 //- /foo/lib.rs crate:foo
968 macro_rules! _mac { () => { () } }
969 pub use crate::_mac as mac;
976 fn goto_def_for_assoc_ty_in_path() {
984 fn f() -> impl Iterator<Item$0 = u8> {}
990 fn unknown_assoc_ty() {
993 trait Iterator { type Item; }
994 fn f() -> impl Iterator<Invalid$0 = u8> {}
1000 fn goto_def_for_assoc_ty_in_path_multiple() {
1009 fn f() -> impl Iterator<A$0 = u8, B = ()> {}
1020 fn f() -> impl Iterator<A = u8, B$0 = ()> {}
1026 fn goto_def_for_assoc_ty_ufcs() {
1034 fn g() -> <() as Iterator<Item$0 = ()>>::Item {}
1040 fn goto_def_for_assoc_ty_ufcs_multiple() {
1049 fn g() -> <() as Iterator<A$0 = (), B = u8>>::B {}
1060 fn g() -> <() as Iterator<A = (), B$0 = u8>>::A {}
1066 fn goto_self_param_ty_specified() {
1072 fn bar(self: &Foo) {
1081 fn goto_self_param_on_decl() {
1095 fn goto_lifetime_param_on_decl() {
1098 fn foo<'foobar$0>(_: &'foobar ()) {
1105 fn goto_lifetime_param_decl() {
1108 fn foo<'foobar>(_: &'foobar$0 ()) {
1115 fn goto_lifetime_param_decl_nested() {
1118 fn foo<'foobar>(_: &'foobar ()) {
1119 fn foo<'foobar>(_: &'foobar$0 ()) {}
1126 fn goto_lifetime_hrtb() {
1127 // FIXME: requires the HIR to somehow track these hrtb lifetimes
1130 fn foo<T>() where for<'a> T: Foo<&'a$0 (u8, u16)>, {}
1136 fn foo<T>() where for<'a$0> T: Foo<&'a (u8, u16)>, {}
1143 fn goto_lifetime_hrtb_for_type() {
1144 // FIXME: requires ForTypes to be implemented
1147 fn foo<T>() where T: for<'a> Foo<&'a$0 (u8, u16)>, {}
1157 fn foo<'foo>(_: &'foo ()) {
1169 fn goto_def_for_intra_doc_link_same_file() {
1172 /// Blah, [`bar`](bar) .. [`foo`](foo$0) has [`bar`](bar)
1175 /// You might want to see [`std::fs::read()`] too.
1184 fn goto_def_for_intra_doc_link_inner() {
1199 fn goto_incomplete_field() {
1204 fn foo() { A { a$0: }; }
1210 fn goto_proc_macro() {
1213 //- /main.rs crate:main deps:mac
1218 //- /mac.rs crate:mac
1227 fn goto_intra_doc_links() {
1232 /// This is the item. Cool!
1237 /// Gives you a [`TheItem$0`].
1239 /// [`TheItem`]: theitem::TheItem
1240 pub fn gimme() -> theitem::TheItem {
1248 fn goto_ident_from_pat_macro() {
1252 ($name:ident) => { Enum::Variant1($name) }
1266 Enum::Variant2 => {}
1279 let str = include_str!("foo.txt$0");
1289 fn goto_def_of_trait_impl_fn() {
1299 impl Twait for Stwuct {
1307 fn goto_def_of_trait_impl_const() {
1317 impl Twait for Stwuct {
1318 const NOMS$0: bool = true;
1325 fn goto_def_of_trait_impl_type_alias() {
1335 impl Twait for Stwuct {