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