]> git.lizzy.rs Git - rust.git/blob - crates/ide/src/goto_implementation.rs
Gifs and screenshots for features in manual
[rust.git] / crates / ide / src / goto_implementation.rs
1 use hir::{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     let def = match def {
40         Definition::ModuleDef(def) => def,
41         _ => return None,
42     };
43     let navs = match def {
44         hir::ModuleDef::Trait(trait_) => impls_for_trait(&sema, trait_),
45         hir::ModuleDef::Adt(adt) => impls_for_ty(&sema, adt.ty(sema.db)),
46         hir::ModuleDef::TypeAlias(alias) => impls_for_ty(&sema, alias.ty(sema.db)),
47         hir::ModuleDef::BuiltinType(builtin) => {
48             let module = sema.to_module_def(position.file_id)?;
49             impls_for_ty(&sema, builtin.ty(sema.db, module))
50         }
51         _ => return None,
52     };
53     Some(RangeInfo { range: node.syntax().text_range(), info: navs })
54 }
55
56 fn impls_for_ty(sema: &Semantics<RootDatabase>, ty: hir::Type) -> Vec<NavigationTarget> {
57     Impl::all_for_type(sema.db, ty).into_iter().filter_map(|imp| imp.try_to_nav(sema.db)).collect()
58 }
59
60 fn impls_for_trait(sema: &Semantics<RootDatabase>, trait_: hir::Trait) -> Vec<NavigationTarget> {
61     Impl::all_for_trait(sema.db, trait_)
62         .into_iter()
63         .filter_map(|imp| imp.try_to_nav(sema.db))
64         .collect()
65 }
66
67 #[cfg(test)]
68 mod tests {
69     use ide_db::base_db::FileRange;
70
71     use crate::fixture;
72
73     fn check(ra_fixture: &str) {
74         let (analysis, position, annotations) = fixture::annotations(ra_fixture);
75
76         let navs = analysis.goto_implementation(position).unwrap().unwrap().info;
77
78         let key = |frange: &FileRange| (frange.file_id, frange.range.start());
79
80         let mut expected = annotations
81             .into_iter()
82             .map(|(range, data)| {
83                 assert!(data.is_empty());
84                 range
85             })
86             .collect::<Vec<_>>();
87         expected.sort_by_key(key);
88
89         let mut actual = navs
90             .into_iter()
91             .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
92             .collect::<Vec<_>>();
93         actual.sort_by_key(key);
94
95         assert_eq!(expected, actual);
96     }
97
98     #[test]
99     fn goto_implementation_works() {
100         check(
101             r#"
102 struct Foo$0;
103 impl Foo {}
104    //^^^
105 "#,
106         );
107     }
108
109     #[test]
110     fn goto_implementation_works_multiple_blocks() {
111         check(
112             r#"
113 struct Foo$0;
114 impl Foo {}
115    //^^^
116 impl Foo {}
117    //^^^
118 "#,
119         );
120     }
121
122     #[test]
123     fn goto_implementation_works_multiple_mods() {
124         check(
125             r#"
126 struct Foo$0;
127 mod a {
128     impl super::Foo {}
129        //^^^^^^^^^^
130 }
131 mod b {
132     impl super::Foo {}
133        //^^^^^^^^^^
134 }
135 "#,
136         );
137     }
138
139     #[test]
140     fn goto_implementation_works_multiple_files() {
141         check(
142             r#"
143 //- /lib.rs
144 struct Foo$0;
145 mod a;
146 mod b;
147 //- /a.rs
148 impl crate::Foo {}
149    //^^^^^^^^^^
150 //- /b.rs
151 impl crate::Foo {}
152    //^^^^^^^^^^
153 "#,
154         );
155     }
156
157     #[test]
158     fn goto_implementation_for_trait() {
159         check(
160             r#"
161 trait T$0 {}
162 struct Foo;
163 impl T for Foo {}
164          //^^^
165 "#,
166         );
167     }
168
169     #[test]
170     fn goto_implementation_for_trait_multiple_files() {
171         check(
172             r#"
173 //- /lib.rs
174 trait T$0 {};
175 struct Foo;
176 mod a;
177 mod b;
178 //- /a.rs
179 impl crate::T for crate::Foo {}
180                 //^^^^^^^^^^
181 //- /b.rs
182 impl crate::T for crate::Foo {}
183                 //^^^^^^^^^^
184             "#,
185         );
186     }
187
188     #[test]
189     fn goto_implementation_all_impls() {
190         check(
191             r#"
192 //- /lib.rs
193 trait T {}
194 struct Foo$0;
195 impl Foo {}
196    //^^^
197 impl T for Foo {}
198          //^^^
199 impl T for &Foo {}
200          //^^^^
201 "#,
202         );
203     }
204
205     #[test]
206     fn goto_implementation_to_builtin_derive() {
207         check(
208             r#"
209   #[derive(Copy)]
210 //^^^^^^^^^^^^^^^
211 struct Foo$0;
212
213 mod marker {
214     trait Copy {}
215 }
216 #[rustc_builtin_macro]
217 macro Copy {}
218 "#,
219         );
220     }
221
222     #[test]
223     fn goto_implementation_type_alias() {
224         check(
225             r#"
226 struct Foo;
227
228 type Bar$0 = Foo;
229
230 impl Foo {}
231    //^^^
232 impl Bar {}
233    //^^^
234 "#,
235         );
236     }
237
238     #[test]
239     fn goto_implementation_adt_generic() {
240         check(
241             r#"
242 struct Foo$0<T>;
243
244 impl<T> Foo<T> {}
245       //^^^^^^
246 impl Foo<str> {}
247    //^^^^^^^^
248 "#,
249         );
250     }
251
252     #[test]
253     fn goto_implementation_builtin() {
254         check(
255             r#"
256 //- /lib.rs crate:main deps:core
257 fn foo(_: bool$0) {{}}
258 //- /libcore.rs crate:core
259 #[lang = "bool"]
260 impl bool {}
261    //^^^^
262 "#,
263         );
264     }
265 }