]> git.lizzy.rs Git - rust.git/blob - crates/ide/src/goto_implementation.rs
fix: move dir on rename mod
[rust.git] / crates / ide / src / goto_implementation.rs
1 use hir::{AsAssocItem, Impl, Semantics};
2 use ide_db::{
3     defs::{Definition, NameClass, NameRefClass},
4     helpers::pick_best_token,
5     RootDatabase,
6 };
7 use itertools::Itertools;
8 use syntax::{ast, AstNode, SyntaxKind::*, T};
9
10 use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav};
11
12 // Feature: Go to Implementation
13 //
14 // Navigates to the impl block of structs, enums or traits. Also implemented as a code lens.
15 //
16 // |===
17 // | Editor  | Shortcut
18 //
19 // | VS Code | kbd:[Ctrl+F12]
20 // |===
21 //
22 // image::https://user-images.githubusercontent.com/48062697/113065566-02f85480-91b1-11eb-9288-aaad8abd8841.gif[]
23 pub(crate) fn goto_implementation(
24     db: &RootDatabase,
25     position: FilePosition,
26 ) -> Option<RangeInfo<Vec<NavigationTarget>>> {
27     let sema = Semantics::new(db);
28     let source_file = sema.parse(position.file_id);
29     let syntax = source_file.syntax().clone();
30
31     let original_token =
32         pick_best_token(syntax.token_at_offset(position.offset), |kind| match kind {
33             IDENT | T![self] => 1,
34             _ => 0,
35         })?;
36     let range = original_token.text_range();
37     let navs = sema
38         .descend_into_macros(original_token)
39         .into_iter()
40         .filter_map(|token| token.parent().and_then(ast::NameLike::cast))
41         .filter_map(|node| match &node {
42             ast::NameLike::Name(name) => {
43                 NameClass::classify(&sema, name).map(|class| match class {
44                     NameClass::Definition(it) | NameClass::ConstReference(it) => it,
45                     NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
46                         Definition::Local(local_def)
47                     }
48                 })
49             }
50             ast::NameLike::NameRef(name_ref) => {
51                 NameRefClass::classify(&sema, name_ref).map(|class| match class {
52                     NameRefClass::Definition(def) => def,
53                     NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
54                         Definition::Local(local_ref)
55                     }
56                 })
57             }
58             ast::NameLike::Lifetime(_) => None,
59         })
60         .unique()
61         .filter_map(|def| {
62             let navs = match def {
63                 Definition::Trait(trait_) => impls_for_trait(&sema, trait_),
64                 Definition::Adt(adt) => impls_for_ty(&sema, adt.ty(sema.db)),
65                 Definition::TypeAlias(alias) => impls_for_ty(&sema, alias.ty(sema.db)),
66                 Definition::BuiltinType(builtin) => impls_for_ty(&sema, builtin.ty(sema.db)),
67                 Definition::Function(f) => {
68                     let assoc = f.as_assoc_item(sema.db)?;
69                     let name = assoc.name(sema.db)?;
70                     let trait_ = assoc.containing_trait_or_trait_impl(sema.db)?;
71                     impls_for_trait_item(&sema, trait_, name)
72                 }
73                 Definition::Const(c) => {
74                     let assoc = c.as_assoc_item(sema.db)?;
75                     let name = assoc.name(sema.db)?;
76                     let trait_ = assoc.containing_trait_or_trait_impl(sema.db)?;
77                     impls_for_trait_item(&sema, trait_, name)
78                 }
79                 _ => return None,
80             };
81             Some(navs)
82         })
83         .flatten()
84         .collect();
85
86     Some(RangeInfo { range, info: navs })
87 }
88
89 fn impls_for_ty(sema: &Semantics<RootDatabase>, ty: hir::Type) -> Vec<NavigationTarget> {
90     Impl::all_for_type(sema.db, ty).into_iter().filter_map(|imp| imp.try_to_nav(sema.db)).collect()
91 }
92
93 fn impls_for_trait(sema: &Semantics<RootDatabase>, trait_: hir::Trait) -> Vec<NavigationTarget> {
94     Impl::all_for_trait(sema.db, trait_)
95         .into_iter()
96         .filter_map(|imp| imp.try_to_nav(sema.db))
97         .collect()
98 }
99
100 fn impls_for_trait_item(
101     sema: &Semantics<RootDatabase>,
102     trait_: hir::Trait,
103     fun_name: hir::Name,
104 ) -> Vec<NavigationTarget> {
105     Impl::all_for_trait(sema.db, trait_)
106         .into_iter()
107         .filter_map(|imp| {
108             let item = imp.items(sema.db).iter().find_map(|itm| {
109                 let itm_name = itm.name(sema.db)?;
110                 (itm_name == fun_name).then(|| *itm)
111             })?;
112             item.try_to_nav(sema.db)
113         })
114         .collect()
115 }
116
117 #[cfg(test)]
118 mod tests {
119     use ide_db::base_db::FileRange;
120     use itertools::Itertools;
121
122     use crate::fixture;
123
124     fn check(ra_fixture: &str) {
125         let (analysis, position, expected) = fixture::annotations(ra_fixture);
126
127         let navs = analysis.goto_implementation(position).unwrap().unwrap().info;
128
129         let cmp = |frange: &FileRange| (frange.file_id, frange.range.start());
130
131         let actual = navs
132             .into_iter()
133             .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
134             .sorted_by_key(cmp)
135             .collect::<Vec<_>>();
136         let expected =
137             expected.into_iter().map(|(range, _)| range).sorted_by_key(cmp).collect::<Vec<_>>();
138         assert_eq!(expected, actual);
139     }
140
141     #[test]
142     fn goto_implementation_works() {
143         check(
144             r#"
145 struct Foo$0;
146 impl Foo {}
147    //^^^
148 "#,
149         );
150     }
151
152     #[test]
153     fn goto_implementation_works_multiple_blocks() {
154         check(
155             r#"
156 struct Foo$0;
157 impl Foo {}
158    //^^^
159 impl Foo {}
160    //^^^
161 "#,
162         );
163     }
164
165     #[test]
166     fn goto_implementation_works_multiple_mods() {
167         check(
168             r#"
169 struct Foo$0;
170 mod a {
171     impl super::Foo {}
172        //^^^^^^^^^^
173 }
174 mod b {
175     impl super::Foo {}
176        //^^^^^^^^^^
177 }
178 "#,
179         );
180     }
181
182     #[test]
183     fn goto_implementation_works_multiple_files() {
184         check(
185             r#"
186 //- /lib.rs
187 struct Foo$0;
188 mod a;
189 mod b;
190 //- /a.rs
191 impl crate::Foo {}
192    //^^^^^^^^^^
193 //- /b.rs
194 impl crate::Foo {}
195    //^^^^^^^^^^
196 "#,
197         );
198     }
199
200     #[test]
201     fn goto_implementation_for_trait() {
202         check(
203             r#"
204 trait T$0 {}
205 struct Foo;
206 impl T for Foo {}
207          //^^^
208 "#,
209         );
210     }
211
212     #[test]
213     fn goto_implementation_for_trait_multiple_files() {
214         check(
215             r#"
216 //- /lib.rs
217 trait T$0 {};
218 struct Foo;
219 mod a;
220 mod b;
221 //- /a.rs
222 impl crate::T for crate::Foo {}
223                 //^^^^^^^^^^
224 //- /b.rs
225 impl crate::T for crate::Foo {}
226                 //^^^^^^^^^^
227             "#,
228         );
229     }
230
231     #[test]
232     fn goto_implementation_all_impls() {
233         check(
234             r#"
235 //- /lib.rs
236 trait T {}
237 struct Foo$0;
238 impl Foo {}
239    //^^^
240 impl T for Foo {}
241          //^^^
242 impl T for &Foo {}
243          //^^^^
244 "#,
245         );
246     }
247
248     #[test]
249     fn goto_implementation_to_builtin_derive() {
250         check(
251             r#"
252 //- minicore: copy, derive
253   #[derive(Copy)]
254 //^^^^^^^^^^^^^^^
255 struct Foo$0;
256 "#,
257         );
258     }
259
260     #[test]
261     fn goto_implementation_type_alias() {
262         check(
263             r#"
264 struct Foo;
265
266 type Bar$0 = Foo;
267
268 impl Foo {}
269    //^^^
270 impl Bar {}
271    //^^^
272 "#,
273         );
274     }
275
276     #[test]
277     fn goto_implementation_adt_generic() {
278         check(
279             r#"
280 struct Foo$0<T>;
281
282 impl<T> Foo<T> {}
283       //^^^^^^
284 impl Foo<str> {}
285    //^^^^^^^^
286 "#,
287         );
288     }
289
290     #[test]
291     fn goto_implementation_builtin() {
292         check(
293             r#"
294 //- /lib.rs crate:main deps:core
295 fn foo(_: bool$0) {{}}
296 //- /libcore.rs crate:core
297 #[lang = "bool"]
298 impl bool {}
299    //^^^^
300 "#,
301         );
302     }
303
304     #[test]
305     fn goto_implementation_trait_functions() {
306         check(
307             r#"
308 trait Tr {
309     fn f$0();
310 }
311
312 struct S;
313
314 impl Tr for S {
315     fn f() {
316      //^
317         println!("Hello, world!");
318     }
319 }
320 "#,
321         );
322     }
323
324     #[test]
325     fn goto_implementation_trait_assoc_const() {
326         check(
327             r#"
328 trait Tr {
329     const C$0: usize;
330 }
331
332 struct S;
333
334 impl Tr for S {
335     const C: usize = 4;
336         //^
337 }
338 "#,
339         );
340     }
341 }