]> git.lizzy.rs Git - rust.git/blob - crates/ide/src/goto_implementation.rs
Merge #7193
[rust.git] / crates / ide / src / goto_implementation.rs
1 use hir::{Crate, Impl, Semantics};
2 use ide_db::RootDatabase;
3 use syntax::{algo::find_node_at_offset, ast, AstNode};
4
5 use crate::{display::TryToNav, 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 = Impl::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             .filter_map(|imp| imp.try_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 = Impl::for_trait(sema.db, krate, tr);
71
72     Some(impls.into_iter().filter_map(|imp| imp.try_to_nav(sema.db)).collect())
73 }
74
75 #[cfg(test)]
76 mod tests {
77     use ide_db::base_db::FileRange;
78
79     use crate::fixture;
80
81     fn check(ra_fixture: &str) {
82         let (analysis, position, annotations) = fixture::annotations(ra_fixture);
83
84         let navs = analysis.goto_implementation(position).unwrap().unwrap().info;
85
86         let key = |frange: &FileRange| (frange.file_id, frange.range.start());
87
88         let mut expected = annotations
89             .into_iter()
90             .map(|(range, data)| {
91                 assert!(data.is_empty());
92                 range
93             })
94             .collect::<Vec<_>>();
95         expected.sort_by_key(key);
96
97         let mut actual = navs
98             .into_iter()
99             .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
100             .collect::<Vec<_>>();
101         actual.sort_by_key(key);
102
103         assert_eq!(expected, actual);
104     }
105
106     #[test]
107     fn goto_implementation_works() {
108         check(
109             r#"
110 struct Foo$0;
111 impl Foo {}
112    //^^^
113 "#,
114         );
115     }
116
117     #[test]
118     fn goto_implementation_works_multiple_blocks() {
119         check(
120             r#"
121 struct Foo$0;
122 impl Foo {}
123    //^^^
124 impl Foo {}
125    //^^^
126 "#,
127         );
128     }
129
130     #[test]
131     fn goto_implementation_works_multiple_mods() {
132         check(
133             r#"
134 struct Foo$0;
135 mod a {
136     impl super::Foo {}
137        //^^^^^^^^^^
138 }
139 mod b {
140     impl super::Foo {}
141        //^^^^^^^^^^
142 }
143 "#,
144         );
145     }
146
147     #[test]
148     fn goto_implementation_works_multiple_files() {
149         check(
150             r#"
151 //- /lib.rs
152 struct Foo$0;
153 mod a;
154 mod b;
155 //- /a.rs
156 impl crate::Foo {}
157    //^^^^^^^^^^
158 //- /b.rs
159 impl crate::Foo {}
160    //^^^^^^^^^^
161 "#,
162         );
163     }
164
165     #[test]
166     fn goto_implementation_for_trait() {
167         check(
168             r#"
169 trait T$0 {}
170 struct Foo;
171 impl T for Foo {}
172          //^^^
173 "#,
174         );
175     }
176
177     #[test]
178     fn goto_implementation_for_trait_multiple_files() {
179         check(
180             r#"
181 //- /lib.rs
182 trait T$0 {};
183 struct Foo;
184 mod a;
185 mod b;
186 //- /a.rs
187 impl crate::T for crate::Foo {}
188                 //^^^^^^^^^^
189 //- /b.rs
190 impl crate::T for crate::Foo {}
191                 //^^^^^^^^^^
192             "#,
193         );
194     }
195
196     #[test]
197     fn goto_implementation_all_impls() {
198         check(
199             r#"
200 //- /lib.rs
201 trait T {}
202 struct Foo$0;
203 impl Foo {}
204    //^^^
205 impl T for Foo {}
206          //^^^
207 impl T for &Foo {}
208          //^^^^
209 "#,
210         );
211     }
212
213     #[test]
214     fn goto_implementation_to_builtin_derive() {
215         check(
216             r#"
217   #[derive(Copy)]
218 //^^^^^^^^^^^^^^^
219 struct Foo$0;
220
221 mod marker {
222     trait Copy {}
223 }
224 #[rustc_builtin_macro]
225 macro Copy {}
226 "#,
227         );
228     }
229 }