1use hir::{AsAssocItem, Impl, Semantics};
2use ide_db::{
3 RootDatabase,
4 defs::{Definition, NameClass, NameRefClass},
5 helpers::pick_best_token,
6};
7use syntax::{AstNode, SyntaxKind::*, T, ast};
8
9use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav};
10
11pub struct GotoImplementationConfig {
12 pub filter_adjacent_derive_implementations: bool,
13}
14
15pub(crate) fn goto_implementation(
25 db: &RootDatabase,
26 config: &GotoImplementationConfig,
27 FilePosition { file_id, offset }: FilePosition,
28) -> Option<RangeInfo<Vec<NavigationTarget>>> {
29 let sema = Semantics::new(db);
30 let source_file = sema.parse_guess_edition(file_id);
31 let syntax = source_file.syntax().clone();
32
33 let original_token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind {
34 IDENT | T![self] | INT_NUMBER => 1,
35 _ => 0,
36 })?;
37 let range = original_token.text_range();
38 let navs = sema
39 .descend_into_macros_exact(original_token)
40 .iter()
41 .filter_map(|token| {
42 token
43 .parent()
44 .and_then(ast::NameLike::cast)
45 .and_then(|node| match &node {
46 ast::NameLike::Name(name) => {
47 NameClass::classify(&sema, name).and_then(|class| match class {
48 NameClass::Definition(it) | NameClass::ConstReference(it) => Some(it),
49 NameClass::PatFieldShorthand { .. } => None,
50 })
51 }
52 ast::NameLike::NameRef(name_ref) => NameRefClass::classify(&sema, name_ref)
53 .and_then(|class| match class {
54 NameRefClass::Definition(def, _) => Some(def),
55 NameRefClass::FieldShorthand { .. }
56 | NameRefClass::ExternCrateShorthand { .. } => None,
57 }),
58 ast::NameLike::Lifetime(_) => None,
59 })
60 .and_then(|def| {
61 let navs = match def {
62 Definition::Trait(trait_) => impls_for_trait(&sema, trait_),
63 Definition::Adt(adt) => {
64 let mut impls = Impl::all_for_type(db, adt.ty(sema.db));
65 if config.filter_adjacent_derive_implementations {
66 impls.retain(|impl_| {
67 sema.impl_generated_from_derive(*impl_) != Some(adt)
68 });
69 }
70 impls
71 .into_iter()
72 .filter_map(|imp| imp.try_to_nav(&sema))
73 .flatten()
74 .collect()
75 }
76 Definition::TypeAlias(alias) => impls_for_ty(&sema, alias.ty(sema.db)),
77 Definition::BuiltinType(builtin) => {
78 impls_for_ty(&sema, builtin.ty(sema.db))
79 }
80 Definition::Function(f) => {
81 let assoc = f.as_assoc_item(sema.db)?;
82 let name = assoc.name(sema.db)?;
83 let trait_ = assoc.container_or_implemented_trait(sema.db)?;
84 impls_for_trait_item(&sema, trait_, name)
85 }
86 Definition::Const(c) => {
87 let assoc = c.as_assoc_item(sema.db)?;
88 let name = assoc.name(sema.db)?;
89 let trait_ = assoc.container_or_implemented_trait(sema.db)?;
90 impls_for_trait_item(&sema, trait_, name)
91 }
92 _ => return None,
93 };
94 Some(navs)
95 })
96 })
97 .flatten()
98 .collect();
99
100 Some(RangeInfo { range, info: navs })
101}
102
103fn impls_for_ty(sema: &Semantics<'_, RootDatabase>, ty: hir::Type<'_>) -> Vec<NavigationTarget> {
104 Impl::all_for_type(sema.db, ty)
105 .into_iter()
106 .filter_map(|imp| imp.try_to_nav(sema))
107 .flatten()
108 .collect()
109}
110
111fn impls_for_trait(
112 sema: &Semantics<'_, RootDatabase>,
113 trait_: hir::Trait,
114) -> Vec<NavigationTarget> {
115 Impl::all_for_trait(sema.db, trait_)
116 .into_iter()
117 .filter_map(|imp| imp.try_to_nav(sema))
118 .flatten()
119 .collect()
120}
121
122fn impls_for_trait_item(
123 sema: &Semantics<'_, RootDatabase>,
124 trait_: hir::Trait,
125 fun_name: hir::Name,
126) -> Vec<NavigationTarget> {
127 Impl::all_for_trait(sema.db, trait_)
128 .into_iter()
129 .filter_map(|imp| {
130 let item = imp.items(sema.db).iter().find_map(|itm| {
131 let itm_name = itm.name(sema.db)?;
132 (itm_name == fun_name).then_some(*itm)
133 })?;
134 item.try_to_nav(sema)
135 })
136 .flatten()
137 .collect()
138}
139
140#[cfg(test)]
141mod tests {
142 use ide_db::FileRange;
143 use itertools::Itertools;
144
145 use crate::{GotoImplementationConfig, fixture};
146
147 const TEST_CONFIG: &GotoImplementationConfig =
148 &GotoImplementationConfig { filter_adjacent_derive_implementations: false };
149
150 #[track_caller]
151 fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
152 check_with_config(TEST_CONFIG, ra_fixture);
153 }
154
155 #[track_caller]
156 fn check_with_config(
157 config: &GotoImplementationConfig,
158 #[rust_analyzer::rust_fixture] ra_fixture: &str,
159 ) {
160 let (analysis, position, expected) = fixture::annotations(ra_fixture);
161
162 let navs = analysis.goto_implementation(config, position).unwrap().unwrap().info;
163
164 let cmp = |frange: &FileRange| (frange.file_id, frange.range.start());
165
166 let actual = navs
167 .into_iter()
168 .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
169 .sorted_by_key(cmp)
170 .collect::<Vec<_>>();
171 let expected =
172 expected.into_iter().map(|(range, _)| range).sorted_by_key(cmp).collect::<Vec<_>>();
173 assert_eq!(expected, actual);
174 }
175
176 #[test]
177 fn goto_implementation_works() {
178 check(
179 r#"
180struct Foo$0;
181impl Foo {}
182 //^^^
183"#,
184 );
185 }
186
187 #[test]
188 fn goto_implementation_works_multiple_blocks() {
189 check(
190 r#"
191struct Foo$0;
192impl Foo {}
193 //^^^
194impl Foo {}
195 //^^^
196"#,
197 );
198 }
199
200 #[test]
201 fn goto_implementation_works_multiple_mods() {
202 check(
203 r#"
204struct Foo$0;
205mod a {
206 impl super::Foo {}
207 //^^^^^^^^^^
208}
209mod b {
210 impl super::Foo {}
211 //^^^^^^^^^^
212}
213"#,
214 );
215 }
216
217 #[test]
218 fn goto_implementation_works_multiple_files() {
219 check(
220 r#"
221//- /lib.rs
222struct Foo$0;
223mod a;
224mod b;
225//- /a.rs
226impl crate::Foo {}
227 //^^^^^^^^^^
228//- /b.rs
229impl crate::Foo {}
230 //^^^^^^^^^^
231"#,
232 );
233 }
234
235 #[test]
236 fn goto_implementation_for_trait() {
237 check(
238 r#"
239trait T$0 {}
240struct Foo;
241impl T for Foo {}
242 //^^^
243"#,
244 );
245 }
246
247 #[test]
248 fn goto_implementation_for_trait_multiple_files() {
249 check(
250 r#"
251//- /lib.rs
252trait T$0 {};
253struct Foo;
254mod a;
255mod b;
256//- /a.rs
257impl crate::T for crate::Foo {}
258 //^^^^^^^^^^
259//- /b.rs
260impl crate::T for crate::Foo {}
261 //^^^^^^^^^^
262 "#,
263 );
264 }
265
266 #[test]
268 fn goto_implementation_all_impls() {
269 check(
270 r#"
271//- /lib.rs
272trait T {}
273struct Foo$0;
274impl Foo {}
275 //^^^
276impl T for Foo {}
277 //^^^
278impl T for &Foo {}
279"#,
280 );
281 }
282
283 #[test]
284 fn goto_implementation_to_builtin_derive() {
285 check(
286 r#"
287//- minicore: copy, derive
288 #[derive(Copy)]
289 //^^^^
290struct Foo$0;
291"#,
292 );
293 }
294
295 #[test]
296 fn goto_implementation_type_alias() {
297 check(
298 r#"
299struct Foo;
300
301type Bar$0 = Foo;
302
303impl Foo {}
304 //^^^
305impl Bar {}
306 //^^^
307"#,
308 );
309 }
310
311 #[test]
312 fn goto_implementation_adt_generic() {
313 check(
314 r#"
315struct Foo$0<T>;
316
317impl<T> Foo<T> {}
318 //^^^^^^
319impl Foo<str> {}
320 //^^^^^^^^
321"#,
322 );
323 }
324
325 #[test]
326 fn goto_implementation_builtin() {
327 check(
328 r#"
329//- /lib.rs crate:main deps:core
330fn foo(_: bool$0) {{}}
331//- /libcore.rs crate:core
332#![rustc_coherence_is_core]
333#[lang = "bool"]
334impl bool {}
335 //^^^^
336"#,
337 );
338 }
339
340 #[test]
341 fn goto_implementation_trait_functions() {
342 check(
343 r#"
344trait Tr {
345 fn f$0();
346}
347
348struct S;
349
350impl Tr for S {
351 fn f() {
352 //^
353 println!("Hello, world!");
354 }
355}
356"#,
357 );
358 }
359
360 #[test]
361 fn goto_implementation_trait_assoc_const() {
362 check(
363 r#"
364trait Tr {
365 const C$0: usize;
366}
367
368struct S;
369
370impl Tr for S {
371 const C: usize = 4;
372 //^
373}
374"#,
375 );
376 }
377
378 #[test]
379 fn goto_adt_implementation_inside_block() {
380 check(
381 r#"
382//- minicore: copy, derive
383trait Bar {}
384
385fn test() {
386 #[derive(Copy)]
387 //^^^^^^^^^^^^^^^
388 struct Foo$0;
389
390 impl Foo {}
391 //^^^
392
393 trait Baz {}
394
395 impl Bar for Foo {}
396 //^^^
397
398 impl Baz for Foo {}
399 //^^^
400}
401"#,
402 );
403 }
404
405 #[test]
406 fn goto_trait_implementation_inside_block() {
407 check(
408 r#"
409struct Bar;
410
411fn test() {
412 trait Foo$0 {}
413
414 struct Baz;
415
416 impl Foo for Bar {}
417 //^^^
418
419 impl Foo for Baz {}
420 //^^^
421}
422"#,
423 );
424 check(
425 r#"
426struct Bar;
427
428fn test() {
429 trait Foo {
430 fn foo$0() {}
431 }
432
433 struct Baz;
434
435 impl Foo for Bar {
436 fn foo() {}
437 //^^^
438 }
439
440 impl Foo for Baz {
441 fn foo() {}
442 //^^^
443 }
444}
445"#,
446 );
447 }
448
449 #[test]
450 fn filter_adjacent_derives() {
451 check_with_config(
452 &GotoImplementationConfig { filter_adjacent_derive_implementations: true },
453 r#"
454//- minicore: clone, copy, derive
455
456#[derive(Clone, Copy)]
457struct Foo$0;
458
459trait Bar {}
460
461impl Bar for Foo {}
462 // ^^^
463 "#,
464 );
465 }
466}