2 use hir::{HasAttrs, ModuleDef, Semantics};
4 defs::{Definition, NameClass, NameRefClass},
8 ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextSize, TokenAtOffset, T,
12 display::TryToNav, doc_links::extract_definitions_from_markdown, runnables::doc_owner_to_def,
13 FilePosition, NavigationTarget, RangeInfo,
16 // Feature: Go to Definition
18 // Navigates to the definition of an identifier.
21 // | Editor | Shortcut
23 // | VS Code | kbd:[F12]
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();
31 let original_token = pick_best(file.token_at_offset(position.offset))?;
32 let token = sema.descend_into_macros(original_token.clone());
33 let parent = token.parent();
34 if let Some(comment) = ast::Comment::cast(token) {
35 let nav = def_for_doc_comment(&sema, position, &comment)?.try_to_nav(db)?;
36 return Some(RangeInfo::new(original_token.text_range(), vec![nav]));
39 let nav = match_ast! {
41 ast::NameRef(name_ref) => {
42 reference_definition(&sema, Either::Right(&name_ref))
45 let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db);
46 def.try_to_nav(sema.db)
48 ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, <) {
49 let def = name_class.referenced_or_defined(sema.db);
50 def.try_to_nav(sema.db)
52 reference_definition(&sema, Either::Left(<))
58 Some(RangeInfo::new(original_token.text_range(), nav.into_iter().collect()))
61 fn def_for_doc_comment(
62 sema: &Semantics<RootDatabase>,
63 position: FilePosition,
64 doc_comment: &ast::Comment,
65 ) -> Option<hir::ModuleDef> {
66 let parent = doc_comment.syntax().parent();
67 let (link, ns) = extract_positioned_link_from_comment(position, doc_comment)?;
69 let def = doc_owner_to_def(sema, parent)?;
71 Definition::ModuleDef(def) => match def {
72 ModuleDef::Module(it) => it.resolve_doc_path(sema.db, &link, ns),
73 ModuleDef::Function(it) => it.resolve_doc_path(sema.db, &link, ns),
74 ModuleDef::Adt(it) => it.resolve_doc_path(sema.db, &link, ns),
75 ModuleDef::Variant(it) => it.resolve_doc_path(sema.db, &link, ns),
76 ModuleDef::Const(it) => it.resolve_doc_path(sema.db, &link, ns),
77 ModuleDef::Static(it) => it.resolve_doc_path(sema.db, &link, ns),
78 ModuleDef::Trait(it) => it.resolve_doc_path(sema.db, &link, ns),
79 ModuleDef::TypeAlias(it) => it.resolve_doc_path(sema.db, &link, ns),
80 ModuleDef::BuiltinType(_) => return None,
82 Definition::Macro(it) => it.resolve_doc_path(sema.db, &link, ns),
83 Definition::Field(it) => it.resolve_doc_path(sema.db, &link, ns),
84 Definition::SelfType(_)
85 | Definition::Local(_)
86 | Definition::GenericParam(_)
87 | Definition::Label(_) => return None,
91 fn extract_positioned_link_from_comment(
92 position: FilePosition,
93 comment: &ast::Comment,
94 ) -> Option<(String, Option<hir::Namespace>)> {
95 let comment_range = comment.syntax().text_range();
96 let doc_comment = comment.doc_comment()?;
97 let def_links = extract_definitions_from_markdown(doc_comment);
98 let (def_link, ns, _) = def_links.iter().min_by_key(|(_, _, def_link_range)| {
99 let matched_position = comment_range.start() + TextSize::from(def_link_range.start as u32);
100 match position.offset.checked_sub(matched_position) {
101 Some(distance) => distance,
102 None => comment_range.end(),
105 Some((def_link.to_string(), ns.clone()))
108 fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
109 return tokens.max_by_key(priority);
110 fn priority(n: &SyntaxToken) -> usize {
112 IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | COMMENT => 2,
113 kind if kind.is_trivia() => 0,
119 pub(crate) fn reference_definition(
120 sema: &Semantics<RootDatabase>,
121 name_ref: Either<&ast::Lifetime, &ast::NameRef>,
122 ) -> Option<NavigationTarget> {
123 let name_kind = name_ref.either(
124 |lifetime| NameRefClass::classify_lifetime(sema, lifetime),
125 |name_ref| NameRefClass::classify(sema, name_ref),
127 let def = name_kind.referenced(sema.db);
128 def.try_to_nav(sema.db)
133 use ide_db::base_db::FileRange;
137 fn check(ra_fixture: &str) {
138 let (analysis, position, expected) = fixture::nav_target_annotation(ra_fixture);
140 analysis.goto_definition(position).unwrap().expect("no definition found").info;
142 panic!("unresolved reference")
144 assert_eq!(navs.len(), 1);
146 let nav = navs.pop().unwrap();
147 assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() });
151 fn goto_def_for_extern_crate() {
154 //- /main.rs crate:main deps:std
156 //- /std/lib.rs crate:std
164 fn goto_def_for_renamed_extern_crate() {
167 //- /main.rs crate:main deps:std
168 extern crate std as abc$0;
169 //- /std/lib.rs crate:std
177 fn goto_def_in_items() {
188 fn goto_def_at_start_of_item() {
199 fn goto_definition_resolves_correct_name() {
218 fn goto_def_for_module_declaration() {
243 fn goto_def_for_macros() {
246 macro_rules! foo { () => { () } }
256 fn goto_def_for_macros_from_other_crates() {
259 //- /lib.rs crate:main deps:foo
265 //- /foo/lib.rs crate:foo
267 macro_rules! foo { () => { () } }
274 fn goto_def_for_macros_in_use_tree() {
277 //- /lib.rs crate:main deps:foo
280 //- /foo/lib.rs crate:foo
282 macro_rules! foo { () => { () } }
289 fn goto_def_for_macro_defined_fn_with_arg() {
293 macro_rules! define_fn {
294 ($name:ident) => (fn $name() {})
308 fn goto_def_for_macro_defined_fn_no_arg() {
312 macro_rules! define_fn {
327 fn goto_definition_works_for_macro_inside_pattern() {
331 macro_rules! foo {() => {0}}
344 fn goto_definition_works_for_macro_inside_match_arm_lhs() {
348 macro_rules! foo {() => {0}}
360 fn goto_def_for_use_alias() {
363 //- /lib.rs crate:main deps:foo
366 //- /foo/lib.rs crate:foo
374 fn goto_def_for_use_alias_foo_macro() {
377 //- /lib.rs crate:main deps:foo
378 use foo::foo as bar$0;
380 //- /foo/lib.rs crate:foo
382 macro_rules! foo { () => { () } }
389 fn goto_def_for_methods() {
394 fn frobnicate(&self) { }
406 fn goto_def_for_fields() {
421 fn goto_def_for_record_fields() {
439 fn goto_def_for_record_pat_fields() {
447 fn bar(foo: Foo) -> Foo {
448 let Foo { spam$0: _, } = foo
455 fn goto_def_for_record_fields_macros() {
458 macro_rules! m { () => { 92 };}
459 struct Foo { spam: u32 }
470 fn goto_for_tuple_fields() {
485 fn goto_def_for_ufcs_inherent_methods() {
501 fn goto_def_for_ufcs_trait_methods_through_traits() {
516 fn goto_def_for_ufcs_trait_methods_through_self() {
523 impl Trait for Foo {}
533 fn goto_definition_on_self() {
539 pub fn new() -> Self {
550 pub fn new() -> Self$0 {
562 pub fn new() -> Self$0 {
574 pub fn thing(a: &Self$0) {
582 fn goto_definition_on_self_in_trait_impl() {
615 fn goto_def_when_used_on_definition_name_itself() {
618 struct Foo$0 { value: u32 }
640 enum Foo$0 { Variant }
658 static INNER$0: &str = "";
665 const INNER$0: &str = "";
672 type Thing$0 = Option<()>;
693 fn goto_from_macro() {
697 ($($tt:tt)*) => { $($tt)* }
706 mod confuse_index { fn foo(); }
712 fn goto_through_format() {
716 macro_rules! format {
717 ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*)))
719 #[rustc_builtin_macro]
721 macro_rules! format_args {
722 ($fmt:expr) => ({ /* compiler built-in */ });
723 ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
726 pub use crate::format_args;
727 fn foo() {} // for index confusion
732 format!("{}", fo$0o())
739 fn goto_through_included_file() {
743 #[rustc_builtin_macro]
744 macro_rules! include {}
747 //^^^^^^^^^^^^^^^^^^^
764 fn goto_for_type_param() {
767 struct Foo<T: Clone> { t: $0T }
774 fn goto_within_macro() {
778 ($($tt:tt)*) => ($($tt)*)
795 ($($tt:tt)*) => ($($tt)*)
811 fn goto_def_in_local_fn() {
826 fn goto_def_in_local_macro() {
830 macro_rules! foo { () => { () } }
839 fn goto_def_for_field_init_shorthand() {
842 struct Foo { x: i32 }
853 fn goto_def_for_enum_variant_field() {
861 Foo::Bar { x$0 } => x
869 fn goto_def_for_enum_variant_self_pattern_const() {
876 match self { Self::Bar$0 => {} }
884 fn goto_def_for_enum_variant_self_pattern_record() {
887 enum Foo { Bar { val: i32 } }
890 fn baz(self) -> i32 {
891 match self { Self::Bar$0 { val } => {} }
899 fn goto_def_for_enum_variant_self_expr_const() {
905 fn baz(self) { Self::Bar$0; }
912 fn goto_def_for_enum_variant_self_expr_record() {
915 enum Foo { Bar { val: i32 } }
918 fn baz(self) { Self::Bar$0 {val: 4}; }
925 fn goto_def_for_type_alias_generic_parameter() {
935 fn goto_def_for_macro_container() {
938 //- /lib.rs crate:main deps:foo
939 foo::module$0::mac!();
941 //- /foo/lib.rs crate:foo
945 macro_rules! _mac { () => { () } }
946 pub use crate::_mac as mac;
953 fn goto_def_for_assoc_ty_in_path() {
961 fn f() -> impl Iterator<Item$0 = u8> {}
967 fn goto_def_for_assoc_ty_in_path_multiple() {
976 fn f() -> impl Iterator<A$0 = u8, B = ()> {}
987 fn f() -> impl Iterator<A = u8, B$0 = ()> {}
993 fn goto_def_for_assoc_ty_ufcs() {
1001 fn g() -> <() as Iterator<Item$0 = ()>>::Item {}
1007 fn goto_def_for_assoc_ty_ufcs_multiple() {
1016 fn g() -> <() as Iterator<A$0 = (), B = u8>>::B {}
1027 fn g() -> <() as Iterator<A = (), B$0 = u8>>::A {}
1033 fn goto_self_param_ty_specified() {
1039 fn bar(self: &Foo) {
1048 fn goto_self_param_on_decl() {
1062 fn goto_lifetime_param_on_decl() {
1065 fn foo<'foobar$0>(_: &'foobar ()) {
1072 fn goto_lifetime_param_decl() {
1075 fn foo<'foobar>(_: &'foobar$0 ()) {
1082 fn goto_lifetime_param_decl_nested() {
1085 fn foo<'foobar>(_: &'foobar ()) {
1086 fn foo<'foobar>(_: &'foobar$0 ()) {}
1093 #[ignore] // requires the HIR to somehow track these hrtb lifetimes
1094 fn goto_lifetime_hrtb() {
1097 fn foo<T>() where for<'a> T: Foo<&'a$0 (u8, u16)>, {}
1103 fn foo<T>() where for<'a$0> T: Foo<&'a (u8, u16)>, {}
1110 #[ignore] // requires ForTypes to be implemented
1111 fn goto_lifetime_hrtb_for_type() {
1114 fn foo<T>() where T: for<'a> Foo<&'a$0 (u8, u16)>, {}
1124 fn foo<'foo>(_: &'foo ()) {
1136 fn goto_def_for_intra_doc_link_same_file() {
1139 /// Blah, [`bar`](bar) .. [`foo`](foo)$0 has [`bar`](bar)
1142 /// You might want to see [`std::fs::read()`] too.
1151 fn goto_def_for_intra_doc_link_inner() {