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