diff options
Diffstat (limited to 'crates/ra_assists')
-rw-r--r-- | crates/ra_assists/src/handlers/auto_import.rs | 129 |
1 files changed, 120 insertions, 9 deletions
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index a25f0650d..a9778fab7 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs | |||
@@ -5,7 +5,13 @@ use crate::{ | |||
5 | assist_ctx::{Assist, AssistCtx}, | 5 | assist_ctx::{Assist, AssistCtx}, |
6 | insert_use_statement, AssistId, | 6 | insert_use_statement, AssistId, |
7 | }; | 7 | }; |
8 | use hir::{db::HirDatabase, Adt, ModPath, Module, ModuleDef, PathResolution, SourceAnalyzer}; | 8 | use ast::{FnDefOwner, ModuleItem, ModuleItemOwner}; |
9 | use hir::{ | ||
10 | db::{DefDatabase, HirDatabase}, | ||
11 | Adt, AssocContainerId, Crate, Function, HasSource, InFile, ModPath, Module, ModuleDef, | ||
12 | PathResolution, SourceAnalyzer, SourceBinder, Trait, | ||
13 | }; | ||
14 | use rustc_hash::FxHashSet; | ||
9 | use std::collections::BTreeSet; | 15 | use std::collections::BTreeSet; |
10 | 16 | ||
11 | // Assist: auto_import | 17 | // Assist: auto_import |
@@ -135,21 +141,88 @@ impl ImportCandidate { | |||
135 | ImportsLocator::new(db) | 141 | ImportsLocator::new(db) |
136 | .find_imports(&self.get_search_query()) | 142 | .find_imports(&self.get_search_query()) |
137 | .into_iter() | 143 | .into_iter() |
138 | .filter_map(|module_def| match self { | 144 | .map(|module_def| match self { |
139 | ImportCandidate::TraitFunction(function_callee, _) => { | 145 | ImportCandidate::TraitFunction(function_callee, _) => { |
140 | if let ModuleDef::Function(function) = module_def { | 146 | let mut applicable_traits = Vec::new(); |
141 | dbg!(function); | 147 | if let ModuleDef::Function(located_function) = module_def { |
142 | todo!() | 148 | let trait_candidates = Self::get_trait_candidates( |
143 | } else { | 149 | db, |
144 | None | 150 | located_function, |
151 | module_with_name_to_import.krate(), | ||
152 | ) | ||
153 | .into_iter() | ||
154 | .map(|trait_candidate| trait_candidate.into()) | ||
155 | .collect(); | ||
156 | |||
157 | function_callee.ty(db).iterate_path_candidates( | ||
158 | db, | ||
159 | module_with_name_to_import.krate(), | ||
160 | &trait_candidates, | ||
161 | None, | ||
162 | |_, assoc| { | ||
163 | if let AssocContainerId::TraitId(trait_id) = assoc.container(db) { | ||
164 | applicable_traits.push( | ||
165 | module_with_name_to_import | ||
166 | .find_use_path(db, ModuleDef::Trait(trait_id.into())), | ||
167 | ); | ||
168 | }; | ||
169 | None::<()> | ||
170 | }, | ||
171 | ); | ||
145 | } | 172 | } |
173 | applicable_traits | ||
146 | } | 174 | } |
147 | _ => module_with_name_to_import.find_use_path(db, module_def), | 175 | _ => vec![module_with_name_to_import.find_use_path(db, module_def)], |
148 | }) | 176 | }) |
177 | .flatten() | ||
178 | .filter_map(std::convert::identity) | ||
149 | .filter(|use_path| !use_path.segments.is_empty()) | 179 | .filter(|use_path| !use_path.segments.is_empty()) |
150 | .take(20) | 180 | .take(20) |
151 | .collect::<BTreeSet<_>>() | 181 | .collect::<BTreeSet<_>>() |
152 | } | 182 | } |
183 | |||
184 | fn get_trait_candidates( | ||
185 | db: &RootDatabase, | ||
186 | called_function: Function, | ||
187 | root_crate: Crate, | ||
188 | ) -> FxHashSet<Trait> { | ||
189 | let mut source_binder = SourceBinder::new(db); | ||
190 | root_crate | ||
191 | .dependencies(db) | ||
192 | .into_iter() | ||
193 | .map(|dependency| db.crate_def_map(dependency.krate.into())) | ||
194 | .chain(std::iter::once(db.crate_def_map(root_crate.into()))) | ||
195 | .map(|crate_def_map| { | ||
196 | crate_def_map | ||
197 | .modules | ||
198 | .iter() | ||
199 | .filter_map(|(_, module_data)| module_data.declaration_source(db)) | ||
200 | .filter_map(|in_file_module| { | ||
201 | Some((in_file_module.file_id, in_file_module.value.item_list()?.items())) | ||
202 | }) | ||
203 | .map(|(file_id, item_list)| { | ||
204 | let mut if_file_trait_defs = Vec::new(); | ||
205 | for module_item in item_list { | ||
206 | if let ModuleItem::TraitDef(trait_def) = module_item { | ||
207 | if let Some(item_list) = trait_def.item_list() { | ||
208 | if item_list | ||
209 | .functions() | ||
210 | .any(|fn_def| fn_def == called_function.source(db).value) | ||
211 | { | ||
212 | if_file_trait_defs.push(InFile::new(file_id, trait_def)) | ||
213 | } | ||
214 | } | ||
215 | } | ||
216 | } | ||
217 | if_file_trait_defs | ||
218 | }) | ||
219 | .flatten() | ||
220 | .filter_map(|in_file_trait_def| source_binder.to_def(in_file_trait_def)) | ||
221 | .collect::<FxHashSet<_>>() | ||
222 | }) | ||
223 | .flatten() | ||
224 | .collect() | ||
225 | } | ||
153 | } | 226 | } |
154 | 227 | ||
155 | #[cfg(test)] | 228 | #[cfg(test)] |
@@ -452,7 +525,45 @@ mod tests { | |||
452 | } | 525 | } |
453 | 526 | ||
454 | #[test] | 527 | #[test] |
455 | #[ignore] // TODO kb | 528 | fn not_applicable_for_imported_trait() { |
529 | check_assist_not_applicable( | ||
530 | auto_import, | ||
531 | r" | ||
532 | mod test_mod { | ||
533 | pub trait TestTrait { | ||
534 | fn test_method(&self); | ||
535 | fn test_function(); | ||
536 | } | ||
537 | |||
538 | pub trait TestTrait2 { | ||
539 | fn test_method(&self); | ||
540 | fn test_function(); | ||
541 | } | ||
542 | pub enum TestEnum { | ||
543 | One, | ||
544 | Two, | ||
545 | } | ||
546 | |||
547 | impl TestTrait2 for TestEnum { | ||
548 | fn test_method(&self) {} | ||
549 | fn test_function() {} | ||
550 | } | ||
551 | |||
552 | impl TestTrait for TestEnum { | ||
553 | fn test_method(&self) {} | ||
554 | fn test_function() {} | ||
555 | } | ||
556 | } | ||
557 | |||
558 | use test_mod::TestTrait2; | ||
559 | fn main() { | ||
560 | test_mod::TestEnum::test_function<|>; | ||
561 | } | ||
562 | ", | ||
563 | ) | ||
564 | } | ||
565 | |||
566 | #[test] | ||
456 | fn trait_method() { | 567 | fn trait_method() { |
457 | check_assist( | 568 | check_assist( |
458 | auto_import, | 569 | auto_import, |