aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs129
-rw-r--r--crates/ra_hir/src/code_model.rs18
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};
8use hir::{db::HirDatabase, Adt, ModPath, Module, ModuleDef, PathResolution, SourceAnalyzer}; 8use ast::{FnDefOwner, ModuleItem, ModuleItemOwner};
9use hir::{
10 db::{DefDatabase, HirDatabase},
11 Adt, AssocContainerId, Crate, Function, HasSource, InFile, ModPath, Module, ModuleDef,
12 PathResolution, SourceAnalyzer, SourceBinder, Trait,
13};
14use rustc_hash::FxHashSet;
9use std::collections::BTreeSet; 15use 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
125pub use hir_def::{ 125pub 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};
128use rustc_hash::FxHashSet; 128use 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}