]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs
Auto merge of #107843 - bjorn3:sync_cg_clif-2023-02-09, r=bjorn3
[rust.git] / src / tools / rust-analyzer / crates / ide / src / goto_declaration.rs
1 use hir::{AsAssocItem, Semantics};
2 use ide_db::{
3     defs::{Definition, NameClass, NameRefClass},
4     RootDatabase,
5 };
6 use syntax::{ast, match_ast, AstNode, SyntaxKind::*, T};
7
8 use crate::{
9     goto_definition::goto_definition, navigation_target::TryToNav, FilePosition, NavigationTarget,
10     RangeInfo,
11 };
12
13 // Feature: Go to Declaration
14 //
15 // Navigates to the declaration of an identifier.
16 //
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(
21     db: &RootDatabase,
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)
32         .iter()
33         .filter_map(|token| {
34             let parent = token.parent()?;
35             let def = match_ast! {
36                 match parent {
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),
40                     },
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),
44                     },
45                     _ => None
46                 }
47             };
48             let assoc = match def? {
49                 Definition::Module(module) => {
50                     return Some(NavigationTarget::from_module_to_decl(db, module))
51                 }
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),
55                 _ => None,
56             }?;
57
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)?;
61             item.try_to_nav(db)
62         })
63         .collect();
64
65     if info.is_empty() {
66         goto_definition(db, position)
67     } else {
68         Some(RangeInfo::new(range, info))
69     }
70 }
71
72 #[cfg(test)]
73 mod tests {
74     use ide_db::base_db::FileRange;
75     use itertools::Itertools;
76
77     use crate::fixture;
78
79     fn check(ra_fixture: &str) {
80         let (analysis, position, expected) = fixture::annotations(ra_fixture);
81         let navs = analysis
82             .goto_declaration(position)
83             .unwrap()
84             .expect("no declaration or definition found")
85             .info;
86         if navs.is_empty() {
87             panic!("unresolved reference")
88         }
89
90         let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start());
91         let navs = navs
92             .into_iter()
93             .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
94             .sorted_by_key(cmp)
95             .collect::<Vec<_>>();
96         let expected = expected
97             .into_iter()
98             .map(|(FileRange { file_id, range }, _)| FileRange { file_id, range })
99             .sorted_by_key(cmp)
100             .collect::<Vec<_>>();
101         assert_eq!(expected, navs);
102     }
103
104     #[test]
105     fn goto_decl_module_outline() {
106         check(
107             r#"
108 //- /main.rs
109 mod foo;
110  // ^^^
111 //- /foo.rs
112 use self$0;
113 "#,
114         )
115     }
116
117     #[test]
118     fn goto_decl_module_inline() {
119         check(
120             r#"
121 mod foo {
122  // ^^^
123     use self$0;
124 }
125 "#,
126         )
127     }
128
129     #[test]
130     fn goto_decl_goto_def_fallback() {
131         check(
132             r#"
133 struct Foo;
134     // ^^^
135 impl Foo$0 {}
136 "#,
137         );
138     }
139
140     #[test]
141     fn goto_decl_assoc_item_no_impl_item() {
142         check(
143             r#"
144 trait Trait {
145     const C: () = ();
146        // ^
147 }
148 impl Trait for () {}
149
150 fn main() {
151     <()>::C$0;
152 }
153 "#,
154         );
155     }
156
157     #[test]
158     fn goto_decl_assoc_item() {
159         check(
160             r#"
161 trait Trait {
162     const C: () = ();
163        // ^
164 }
165 impl Trait for () {
166     const C: () = ();
167 }
168
169 fn main() {
170     <()>::C$0;
171 }
172 "#,
173         );
174         check(
175             r#"
176 trait Trait {
177     const C: () = ();
178        // ^
179 }
180 impl Trait for () {
181     const C$0: () = ();
182 }
183 "#,
184         );
185     }
186
187     #[test]
188     fn goto_decl_field_pat_shorthand() {
189         check(
190             r#"
191 struct Foo { field: u32 }
192            //^^^^^
193 fn main() {
194     let Foo { field$0 };
195 }
196 "#,
197         );
198     }
199
200     #[test]
201     fn goto_decl_constructor_shorthand() {
202         check(
203             r#"
204 struct Foo { field: u32 }
205            //^^^^^
206 fn main() {
207     let field = 0;
208     Foo { field$0 };
209 }
210 "#,
211         );
212     }
213 }