aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/goto_implementation.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src/goto_implementation.rs')
-rw-r--r--crates/ide/src/goto_implementation.rs229
1 files changed, 229 insertions, 0 deletions
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs
new file mode 100644
index 000000000..f503f4ec5
--- /dev/null
+++ b/crates/ide/src/goto_implementation.rs
@@ -0,0 +1,229 @@
1use hir::{Crate, ImplDef, Semantics};
2use ide_db::RootDatabase;
3use syntax::{algo::find_node_at_offset, ast, AstNode};
4
5use 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// |===
16pub(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
41fn 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
63fn 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)]
76mod 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#"
112struct Foo<|>;
113impl Foo {}
114 //^^^
115"#,
116 );
117 }
118
119 #[test]
120 fn goto_implementation_works_multiple_blocks() {
121 check(
122 r#"
123struct Foo<|>;
124impl Foo {}
125 //^^^
126impl Foo {}
127 //^^^
128"#,
129 );
130 }
131
132 #[test]
133 fn goto_implementation_works_multiple_mods() {
134 check(
135 r#"
136struct Foo<|>;
137mod a {
138 impl super::Foo {}
139 //^^^^^^^^^^
140}
141mod 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
154struct Foo<|>;
155mod a;
156mod b;
157//- /a.rs
158impl crate::Foo {}
159 //^^^^^^^^^^
160//- /b.rs
161impl crate::Foo {}
162 //^^^^^^^^^^
163"#,
164 );
165 }
166
167 #[test]
168 fn goto_implementation_for_trait() {
169 check(
170 r#"
171trait T<|> {}
172struct Foo;
173impl 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
184trait T<|> {};
185struct Foo;
186mod a;
187mod b;
188//- /a.rs
189impl crate::T for crate::Foo {}
190 //^^^^^^^^^^
191//- /b.rs
192impl 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
203trait T {}
204struct Foo<|>;
205impl Foo {}
206 //^^^
207impl T for Foo {}
208 //^^^
209impl 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//^^^^^^^^^^^^^^^
221struct Foo<|>;
222
223mod marker {
224 trait Copy {}
225}
226"#,
227 );
228 }
229}