1 use hir::{AsAssocItem, Semantics};
3 defs::{Definition, NameClass, NameRefClass},
6 use syntax::{ast, match_ast, AstNode, SyntaxKind::*, T};
9 goto_definition::goto_definition, navigation_target::TryToNav, FilePosition, NavigationTarget,
13 // Feature: Go to Declaration
15 // Navigates to the declaration of an identifier.
17 // This is the same as `Go to Definition` with the following exceptions:
18 // - outline modules will navigate to the `mod name;` item declaration
19 // - trait assoc items will navigate to the assoc item of the trait declaration opposed to the trait impl
20 pub(crate) fn goto_declaration(
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 = file
27 .token_at_offset(position.offset)
28 .find(|it| matches!(it.kind(), IDENT | T![self] | T![super] | T![crate] | T![Self]))?;
29 let range = original_token.text_range();
30 let info: Vec<NavigationTarget> = sema
31 .descend_into_macros(original_token)
34 let parent = token.parent()?;
35 let def = match_ast! {
37 ast::NameRef(name_ref) => match NameRefClass::classify(&sema, &name_ref)? {
38 NameRefClass::Definition(it) => Some(it),
39 NameRefClass::FieldShorthand { field_ref, .. } => return field_ref.try_to_nav(db),
41 ast::Name(name) => match NameClass::classify(&sema, &name)? {
42 NameClass::Definition(it) | NameClass::ConstReference(it) => Some(it),
43 NameClass::PatFieldShorthand { field_ref, .. } => return field_ref.try_to_nav(db),
48 let assoc = match def? {
49 Definition::Module(module) => {
50 return Some(NavigationTarget::from_module_to_decl(db, module))
52 Definition::Const(c) => c.as_assoc_item(db),
53 Definition::TypeAlias(ta) => ta.as_assoc_item(db),
54 Definition::Function(f) => f.as_assoc_item(db),
58 let trait_ = assoc.containing_trait_impl(db)?;
59 let name = Some(assoc.name(db)?);
60 let item = trait_.items(db).into_iter().find(|it| it.name(db) == name)?;
66 goto_definition(db, position)
68 Some(RangeInfo::new(range, info))
74 use ide_db::base_db::FileRange;
75 use itertools::Itertools;
79 fn check(ra_fixture: &str) {
80 let (analysis, position, expected) = fixture::annotations(ra_fixture);
82 .goto_declaration(position)
84 .expect("no declaration or definition found")
87 panic!("unresolved reference")
90 let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start());
93 .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
96 let expected = expected
98 .map(|(FileRange { file_id, range }, _)| FileRange { file_id, range })
100 .collect::<Vec<_>>();
101 assert_eq!(expected, navs);
105 fn goto_decl_module_outline() {
118 fn goto_decl_module_inline() {
130 fn goto_decl_goto_def_fallback() {
141 fn goto_decl_assoc_item_no_impl_item() {
158 fn goto_decl_assoc_item() {
188 fn goto_decl_field_pat_shorthand() {
191 struct Foo { field: u32 }
201 fn goto_decl_constructor_shorthand() {
204 struct Foo { field: u32 }