aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/impls.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/impls.rs')
-rw-r--r--crates/ra_ide/src/impls.rs206
1 files changed, 206 insertions, 0 deletions
diff --git a/crates/ra_ide/src/impls.rs b/crates/ra_ide/src/impls.rs
new file mode 100644
index 000000000..aa480e399
--- /dev/null
+++ b/crates/ra_ide/src/impls.rs
@@ -0,0 +1,206 @@
1//! FIXME: write short doc here
2
3use hir::{FromSource, ImplBlock};
4use ra_db::SourceDatabase;
5use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
6
7use crate::{db::RootDatabase, display::ToNav, FilePosition, NavigationTarget, RangeInfo};
8
9pub(crate) fn goto_implementation(
10 db: &RootDatabase,
11 position: FilePosition,
12) -> Option<RangeInfo<Vec<NavigationTarget>>> {
13 let parse = db.parse(position.file_id);
14 let syntax = parse.tree().syntax().clone();
15
16 let src = hir::ModuleSource::from_position(db, position);
17 let module = hir::Module::from_definition(
18 db,
19 hir::Source { file_id: position.file_id.into(), value: src },
20 )?;
21
22 if let Some(nominal_def) = find_node_at_offset::<ast::NominalDef>(&syntax, position.offset) {
23 return Some(RangeInfo::new(
24 nominal_def.syntax().text_range(),
25 impls_for_def(db, position, &nominal_def, module)?,
26 ));
27 } else if let Some(trait_def) = find_node_at_offset::<ast::TraitDef>(&syntax, position.offset) {
28 return Some(RangeInfo::new(
29 trait_def.syntax().text_range(),
30 impls_for_trait(db, position, &trait_def, module)?,
31 ));
32 }
33
34 None
35}
36
37fn impls_for_def(
38 db: &RootDatabase,
39 position: FilePosition,
40 node: &ast::NominalDef,
41 module: hir::Module,
42) -> Option<Vec<NavigationTarget>> {
43 let ty = match node {
44 ast::NominalDef::StructDef(def) => {
45 let src = hir::Source { file_id: position.file_id.into(), value: def.clone() };
46 hir::Struct::from_source(db, src)?.ty(db)
47 }
48 ast::NominalDef::EnumDef(def) => {
49 let src = hir::Source { file_id: position.file_id.into(), value: def.clone() };
50 hir::Enum::from_source(db, src)?.ty(db)
51 }
52 ast::NominalDef::UnionDef(def) => {
53 let src = hir::Source { file_id: position.file_id.into(), value: def.clone() };
54 hir::Union::from_source(db, src)?.ty(db)
55 }
56 };
57
58 let krate = module.krate();
59 let impls = ImplBlock::all_in_crate(db, krate);
60
61 Some(
62 impls
63 .into_iter()
64 .filter(|impl_block| ty.is_equal_for_find_impls(&impl_block.target_ty(db)))
65 .map(|imp| imp.to_nav(db))
66 .collect(),
67 )
68}
69
70fn impls_for_trait(
71 db: &RootDatabase,
72 position: FilePosition,
73 node: &ast::TraitDef,
74 module: hir::Module,
75) -> Option<Vec<NavigationTarget>> {
76 let src = hir::Source { file_id: position.file_id.into(), value: node.clone() };
77 let tr = hir::Trait::from_source(db, src)?;
78
79 let krate = module.krate();
80 let impls = ImplBlock::for_trait(db, krate, tr);
81
82 Some(impls.into_iter().map(|imp| imp.to_nav(db)).collect())
83}
84
85#[cfg(test)]
86mod tests {
87 use crate::mock_analysis::analysis_and_position;
88
89 fn check_goto(fixture: &str, expected: &[&str]) {
90 let (analysis, pos) = analysis_and_position(fixture);
91
92 let mut navs = analysis.goto_implementation(pos).unwrap().unwrap().info;
93 assert_eq!(navs.len(), expected.len());
94 navs.sort_by_key(|nav| (nav.file_id(), nav.full_range().start()));
95 navs.into_iter().enumerate().for_each(|(i, nav)| nav.assert_match(expected[i]));
96 }
97
98 #[test]
99 fn goto_implementation_works() {
100 check_goto(
101 "
102 //- /lib.rs
103 struct Foo<|>;
104 impl Foo {}
105 ",
106 &["impl IMPL_BLOCK FileId(1) [12; 23)"],
107 );
108 }
109
110 #[test]
111 fn goto_implementation_works_multiple_blocks() {
112 check_goto(
113 "
114 //- /lib.rs
115 struct Foo<|>;
116 impl Foo {}
117 impl Foo {}
118 ",
119 &["impl IMPL_BLOCK FileId(1) [12; 23)", "impl IMPL_BLOCK FileId(1) [24; 35)"],
120 );
121 }
122
123 #[test]
124 fn goto_implementation_works_multiple_mods() {
125 check_goto(
126 "
127 //- /lib.rs
128 struct Foo<|>;
129 mod a {
130 impl super::Foo {}
131 }
132 mod b {
133 impl super::Foo {}
134 }
135 ",
136 &["impl IMPL_BLOCK FileId(1) [24; 42)", "impl IMPL_BLOCK FileId(1) [57; 75)"],
137 );
138 }
139
140 #[test]
141 fn goto_implementation_works_multiple_files() {
142 check_goto(
143 "
144 //- /lib.rs
145 struct Foo<|>;
146 mod a;
147 mod b;
148 //- /a.rs
149 impl crate::Foo {}
150 //- /b.rs
151 impl crate::Foo {}
152 ",
153 &["impl IMPL_BLOCK FileId(2) [0; 18)", "impl IMPL_BLOCK FileId(3) [0; 18)"],
154 );
155 }
156
157 #[test]
158 fn goto_implementation_for_trait() {
159 check_goto(
160 "
161 //- /lib.rs
162 trait T<|> {}
163 struct Foo;
164 impl T for Foo {}
165 ",
166 &["impl IMPL_BLOCK FileId(1) [23; 40)"],
167 );
168 }
169
170 #[test]
171 fn goto_implementation_for_trait_multiple_files() {
172 check_goto(
173 "
174 //- /lib.rs
175 trait T<|> {};
176 struct Foo;
177 mod a;
178 mod b;
179 //- /a.rs
180 impl crate::T for crate::Foo {}
181 //- /b.rs
182 impl crate::T for crate::Foo {}
183 ",
184 &["impl IMPL_BLOCK FileId(2) [0; 31)", "impl IMPL_BLOCK FileId(3) [0; 31)"],
185 );
186 }
187
188 #[test]
189 fn goto_implementation_all_impls() {
190 check_goto(
191 "
192 //- /lib.rs
193 trait T {}
194 struct Foo<|>;
195 impl Foo {}
196 impl T for Foo {}
197 impl T for &Foo {}
198 ",
199 &[
200 "impl IMPL_BLOCK FileId(1) [23; 34)",
201 "impl IMPL_BLOCK FileId(1) [35; 52)",
202 "impl IMPL_BLOCK FileId(1) [53; 71)",
203 ],
204 );
205 }
206}