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