]> git.lizzy.rs Git - rust.git/blob - crates/ide/src/goto_implementation.rs
Merge #5782
[rust.git] / crates / ide / src / goto_implementation.rs
1 use hir::{Crate, ImplDef, Semantics};
2 use ide_db::RootDatabase;
3 use syntax::{algo::find_node_at_offset, ast, AstNode};
4
5 use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo};
6
7 // Feature: Go to Implementation
8 //
9 // Navigates to the impl block of structs, enums or traits. Also implemented as a code lens.
10 //
11 // |===
12 // | Editor  | Shortcut
13 //
14 // | VS Code | kbd:[Ctrl+F12]
15 // |===
16 pub(crate) fn goto_implementation(
17     db: &RootDatabase,
18     position: FilePosition,
19 ) -> Option<RangeInfo<Vec<NavigationTarget>>> {
20     let sema = Semantics::new(db);
21     let source_file = sema.parse(position.file_id);
22     let syntax = source_file.syntax().clone();
23
24     let krate = sema.to_module_def(position.file_id)?.krate();
25
26     if let Some(nominal_def) = find_node_at_offset::<ast::AdtDef>(&syntax, position.offset) {
27         return Some(RangeInfo::new(
28             nominal_def.syntax().text_range(),
29             impls_for_def(&sema, &nominal_def, krate)?,
30         ));
31     } else if let Some(trait_def) = find_node_at_offset::<ast::Trait>(&syntax, position.offset) {
32         return Some(RangeInfo::new(
33             trait_def.syntax().text_range(),
34             impls_for_trait(&sema, &trait_def, krate)?,
35         ));
36     }
37
38     None
39 }
40
41 fn impls_for_def(
42     sema: &Semantics<RootDatabase>,
43     node: &ast::AdtDef,
44     krate: Crate,
45 ) -> Option<Vec<NavigationTarget>> {
46     let ty = match node {
47         ast::AdtDef::Struct(def) => sema.to_def(def)?.ty(sema.db),
48         ast::AdtDef::Enum(def) => sema.to_def(def)?.ty(sema.db),
49         ast::AdtDef::Union(def) => sema.to_def(def)?.ty(sema.db),
50     };
51
52     let impls = ImplDef::all_in_crate(sema.db, krate);
53
54     Some(
55         impls
56             .into_iter()
57             .filter(|impl_def| ty.is_equal_for_find_impls(&impl_def.target_ty(sema.db)))
58             .map(|imp| imp.to_nav(sema.db))
59             .collect(),
60     )
61 }
62
63 fn impls_for_trait(
64     sema: &Semantics<RootDatabase>,
65     node: &ast::Trait,
66     krate: Crate,
67 ) -> Option<Vec<NavigationTarget>> {
68     let tr = sema.to_def(node)?;
69
70     let impls = ImplDef::for_trait(sema.db, krate, tr);
71
72     Some(impls.into_iter().map(|imp| imp.to_nav(sema.db)).collect())
73 }
74
75 #[cfg(test)]
76 mod tests {
77     use base_db::FileRange;
78
79     use crate::mock_analysis::MockAnalysis;
80
81     fn check(ra_fixture: &str) {
82         let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture);
83         let annotations = mock.annotations();
84         let analysis = mock.analysis();
85
86         let navs = analysis.goto_implementation(position).unwrap().unwrap().info;
87
88         let key = |frange: &FileRange| (frange.file_id, frange.range.start());
89
90         let mut expected = annotations
91             .into_iter()
92             .map(|(range, data)| {
93                 assert!(data.is_empty());
94                 range
95             })
96             .collect::<Vec<_>>();
97         expected.sort_by_key(key);
98
99         let mut actual = navs
100             .into_iter()
101             .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
102             .collect::<Vec<_>>();
103         actual.sort_by_key(key);
104
105         assert_eq!(expected, actual);
106     }
107
108     #[test]
109     fn goto_implementation_works() {
110         check(
111             r#"
112 struct Foo<|>;
113 impl Foo {}
114    //^^^
115 "#,
116         );
117     }
118
119     #[test]
120     fn goto_implementation_works_multiple_blocks() {
121         check(
122             r#"
123 struct Foo<|>;
124 impl Foo {}
125    //^^^
126 impl Foo {}
127    //^^^
128 "#,
129         );
130     }
131
132     #[test]
133     fn goto_implementation_works_multiple_mods() {
134         check(
135             r#"
136 struct Foo<|>;
137 mod a {
138     impl super::Foo {}
139        //^^^^^^^^^^
140 }
141 mod b {
142     impl super::Foo {}
143        //^^^^^^^^^^
144 }
145 "#,
146         );
147     }
148
149     #[test]
150     fn goto_implementation_works_multiple_files() {
151         check(
152             r#"
153 //- /lib.rs
154 struct Foo<|>;
155 mod a;
156 mod b;
157 //- /a.rs
158 impl crate::Foo {}
159    //^^^^^^^^^^
160 //- /b.rs
161 impl crate::Foo {}
162    //^^^^^^^^^^
163 "#,
164         );
165     }
166
167     #[test]
168     fn goto_implementation_for_trait() {
169         check(
170             r#"
171 trait T<|> {}
172 struct Foo;
173 impl T for Foo {}
174          //^^^
175 "#,
176         );
177     }
178
179     #[test]
180     fn goto_implementation_for_trait_multiple_files() {
181         check(
182             r#"
183 //- /lib.rs
184 trait T<|> {};
185 struct Foo;
186 mod a;
187 mod b;
188 //- /a.rs
189 impl crate::T for crate::Foo {}
190                 //^^^^^^^^^^
191 //- /b.rs
192 impl crate::T for crate::Foo {}
193                 //^^^^^^^^^^
194             "#,
195         );
196     }
197
198     #[test]
199     fn goto_implementation_all_impls() {
200         check(
201             r#"
202 //- /lib.rs
203 trait T {}
204 struct Foo<|>;
205 impl Foo {}
206    //^^^
207 impl T for Foo {}
208          //^^^
209 impl T for &Foo {}
210          //^^^^
211 "#,
212         );
213     }
214
215     #[test]
216     fn goto_implementation_to_builtin_derive() {
217         check(
218             r#"
219   #[derive(Copy)]
220 //^^^^^^^^^^^^^^^
221 struct Foo<|>;
222
223 mod marker {
224     trait Copy {}
225 }
226 "#,
227         );
228     }
229 }