ra_ap_ide/
goto_type_definition.rs1use hir::GenericParam;
2use ide_db::{base_db::Upcast, defs::Definition, helpers::pick_best_token, RootDatabase};
3use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, T};
4
5use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav};
6
7pub(crate) fn goto_type_definition(
17 db: &RootDatabase,
18 FilePosition { file_id, offset }: FilePosition,
19) -> Option<RangeInfo<Vec<NavigationTarget>>> {
20 let sema = hir::Semantics::new(db);
21
22 let file: ast::SourceFile = sema.parse_guess_edition(file_id);
23 let token: SyntaxToken =
24 pick_best_token(file.syntax().token_at_offset(offset), |kind| match kind {
25 IDENT | INT_NUMBER | T![self] => 3,
26 kind if kind.is_trivia() => 0,
27 T![;] => 1,
28 _ => 2,
29 })?;
30
31 let mut res = Vec::new();
32 let mut push = |def: Definition| {
33 if let Some(navs) = def.try_to_nav(db) {
34 for nav in navs {
35 if !res.contains(&nav) {
36 res.push(nav);
37 }
38 }
39 }
40 };
41 let mut process_ty = |ty: hir::Type| {
42 let ty = ty.strip_references();
44 ty.walk(db, |t| {
45 if let Some(adt) = t.as_adt() {
46 push(adt.into());
47 } else if let Some(trait_) = t.as_dyn_trait() {
48 push(trait_.into());
49 } else if let Some(traits) = t.as_impl_traits(db) {
50 traits.for_each(|it| push(it.into()));
51 } else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
52 push(trait_.into());
53 }
54 });
55 };
56 if let Some((range, resolution)) = sema.check_for_format_args_template(token.clone(), offset) {
57 if let Some(ty) = resolution.and_then(|res| match Definition::from(res) {
58 Definition::Const(it) => Some(it.ty(db)),
59 Definition::Static(it) => Some(it.ty(db)),
60 Definition::GenericParam(GenericParam::ConstParam(it)) => Some(it.ty(db)),
61 Definition::Local(it) => Some(it.ty(db)),
62 Definition::Adt(hir::Adt::Struct(it)) => Some(it.ty(db)),
63 _ => None,
64 }) {
65 process_ty(ty);
66 }
67 return Some(RangeInfo::new(range, res));
68 }
69
70 let range = token.text_range();
71 sema.descend_into_macros_no_opaque(token)
72 .into_iter()
73 .filter_map(|token| {
74 let ty = sema
75 .token_ancestors_with_macros(token)
76 .take_while(|node| !ast::TokenTree::can_cast(node.kind()))
83 .find_map(|node| {
84 let ty = match_ast! {
85 match node {
86 ast::Expr(it) => sema.type_of_expr(&it)?.original,
87 ast::Pat(it) => sema.type_of_pat(&it)?.original,
88 ast::SelfParam(it) => sema.type_of_self(&it)?,
89 ast::Type(it) => sema.resolve_type(&it)?,
90 ast::RecordField(it) => sema.to_def(&it)?.ty(db.upcast()),
91 ast::NameRef(it) => {
93 if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) {
94 let (_, _, ty) = sema.resolve_record_field(&record_field)?;
95 ty
96 } else {
97 let record_field = ast::RecordPatField::for_field_name_ref(&it)?;
98 sema.resolve_record_pat_field(&record_field)?.1
99 }
100 },
101 _ => return None,
102 }
103 };
104
105 Some(ty)
106 });
107 ty
108 })
109 .for_each(process_ty);
110 Some(RangeInfo::new(range, res))
111}
112
113#[cfg(test)]
114mod tests {
115 use ide_db::FileRange;
116 use itertools::Itertools;
117
118 use crate::fixture;
119
120 fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
121 let (analysis, position, expected) = fixture::annotations(ra_fixture);
122 let navs = analysis.goto_type_definition(position).unwrap().unwrap().info;
123 assert!(!navs.is_empty(), "navigation is empty");
124
125 let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start());
126 let navs = navs
127 .into_iter()
128 .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
129 .sorted_by_key(cmp)
130 .collect::<Vec<_>>();
131 let expected = expected
132 .into_iter()
133 .map(|(file_range, _)| file_range)
134 .sorted_by_key(cmp)
135 .collect::<Vec<_>>();
136 assert_eq!(expected, navs);
137 }
138
139 #[test]
140 fn goto_type_definition_works_simple() {
141 check(
142 r#"
143struct Foo;
144 //^^^
145fn foo() {
146 let f: Foo; f$0
147}
148"#,
149 );
150 }
151
152 #[test]
153 fn goto_type_definition_record_expr_field() {
154 check(
155 r#"
156struct Bar;
157 // ^^^
158struct Foo { foo: Bar }
159fn foo() {
160 Foo { foo$0 }
161}
162"#,
163 );
164 check(
165 r#"
166struct Bar;
167 // ^^^
168struct Foo { foo: Bar }
169fn foo() {
170 Foo { foo$0: Bar }
171}
172"#,
173 );
174 }
175
176 #[test]
177 fn goto_type_definition_record_pat_field() {
178 check(
179 r#"
180struct Bar;
181 // ^^^
182struct Foo { foo: Bar }
183fn foo() {
184 let Foo { foo$0 };
185}
186"#,
187 );
188 check(
189 r#"
190struct Bar;
191 // ^^^
192struct Foo { foo: Bar }
193fn foo() {
194 let Foo { foo$0: bar };
195}
196"#,
197 );
198 }
199
200 #[test]
201 fn goto_type_definition_works_simple_ref() {
202 check(
203 r#"
204struct Foo;
205 //^^^
206fn foo() {
207 let f: &Foo; f$0
208}
209"#,
210 );
211 }
212
213 #[test]
214 fn goto_type_definition_works_through_macro() {
215 check(
216 r#"
217macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
218struct Foo {}
219 //^^^
220id! {
221 fn bar() { let f$0 = Foo {}; }
222}
223"#,
224 );
225 }
226
227 #[test]
228 fn dont_collect_type_from_token_in_macro_call() {
229 check(
230 r#"
231struct DontCollectMe;
232struct S;
233 //^
234
235macro_rules! inner {
236 ($t:tt) => { DontCollectMe }
237}
238macro_rules! m {
239 ($t:ident) => {
240 match $t {
241 _ => inner!($t);
242 }
243 }
244}
245
246fn test() {
247 m!($0S);
248}
249"#,
250 );
251 }
252
253 #[test]
254 fn goto_type_definition_for_param() {
255 check(
256 r#"
257struct Foo;
258 //^^^
259fn foo($0f: Foo) {}
260"#,
261 );
262 }
263
264 #[test]
265 fn goto_type_definition_for_tuple_field() {
266 check(
267 r#"
268struct Foo;
269 //^^^
270struct Bar(Foo);
271fn foo() {
272 let bar = Bar(Foo);
273 bar.$00;
274}
275"#,
276 );
277 }
278
279 #[test]
280 fn goto_def_for_self_param() {
281 check(
282 r#"
283struct Foo;
284 //^^^
285impl Foo {
286 fn f(&self$0) {}
287}
288"#,
289 )
290 }
291
292 #[test]
293 fn goto_def_for_type_fallback() {
294 check(
295 r#"
296struct Foo;
297 //^^^
298impl Foo$0 {}
299"#,
300 )
301 }
302
303 #[test]
304 fn goto_def_for_struct_field() {
305 check(
306 r#"
307struct Bar;
308 //^^^
309
310struct Foo {
311 bar$0: Bar,
312}
313"#,
314 );
315 }
316
317 #[test]
318 fn goto_def_for_enum_struct_field() {
319 check(
320 r#"
321struct Bar;
322 //^^^
323
324enum Foo {
325 Bar {
326 bar$0: Bar
327 },
328}
329"#,
330 );
331 }
332
333 #[test]
334 fn goto_def_considers_generics() {
335 check(
336 r#"
337struct Foo;
338 //^^^
339struct Bar<T, U>(T, U);
340 //^^^
341struct Baz<T>(T);
342 //^^^
343
344fn foo(x$0: Bar<Baz<Foo>, Baz<usize>) {}
345"#,
346 );
347 }
348
349 #[test]
350 fn implicit_format_args() {
351 check(
352 r#"
353//- minicore: fmt
354struct Bar;
355 // ^^^
356 fn test() {
357 let a = Bar;
358 format_args!("hello {a$0}");
359}
360"#,
361 );
362 check(
363 r#"
364//- minicore: fmt
365struct Bar;
366 // ^^^
367 fn test() {
368 format_args!("hello {Bar$0}");
369}
370"#,
371 );
372 check(
373 r#"
374//- minicore: fmt
375struct Bar;
376 // ^^^
377const BAR: Bar = Bar;
378fn test() {
379 format_args!("hello {BAR$0}");
380}
381"#,
382 );
383 }
384}