diff options
-rw-r--r-- | crates/ra_assists/src/handlers/auto_import.rs | 129 | ||||
-rw-r--r-- | crates/ra_hir/src/code_model.rs | 18 |
2 files changed, 127 insertions, 20 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, |
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 4fb679f6d..73158b8bd 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -123,7 +123,7 @@ impl_froms!( | |||
123 | ); | 123 | ); |
124 | 124 | ||
125 | pub use hir_def::{ | 125 | pub use hir_def::{ |
126 | attr::Attrs, item_scope::ItemInNs, visibility::Visibility, AssocItemId, AssocItemLoc, | 126 | attr::Attrs, item_scope::ItemInNs, visibility::Visibility, AssocContainerId, AssocItemId, |
127 | }; | 127 | }; |
128 | use rustc_hash::FxHashSet; | 128 | use rustc_hash::FxHashSet; |
129 | 129 | ||
@@ -696,16 +696,12 @@ impl AssocItem { | |||
696 | AssocItem::TypeAlias(t) => t.module(db), | 696 | AssocItem::TypeAlias(t) => t.module(db), |
697 | } | 697 | } |
698 | } | 698 | } |
699 | pub fn container(self, db: &impl DefDatabase) -> AssocItemContainer { | 699 | |
700 | let container = match self { | 700 | pub fn container(self, db: &impl DefDatabase) -> AssocContainerId { |
701 | AssocItem::Function(it) => it.id.lookup(db).container, | 701 | match self { |
702 | AssocItem::Const(it) => it.id.lookup(db).container, | 702 | AssocItem::Function(f) => f.id.lookup(db).container, |
703 | AssocItem::TypeAlias(it) => it.id.lookup(db).container, | 703 | AssocItem::Const(c) => c.id.lookup(db).container, |
704 | }; | 704 | AssocItem::TypeAlias(t) => t.id.lookup(db).container, |
705 | match container { | ||
706 | AssocContainerId::TraitId(id) => AssocItemContainer::Trait(id.into()), | ||
707 | AssocContainerId::ImplId(id) => AssocItemContainer::ImplBlock(id.into()), | ||
708 | AssocContainerId::ContainerId(_) => panic!("invalid AssocItem"), | ||
709 | } | 705 | } |
710 | } | 706 | } |
711 | } | 707 | } |