From 24ab3e80ca258a3db21bf263225c52d9995a2ea0 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 10 Feb 2020 00:22:12 +0200 Subject: Resolve methods and functions better --- crates/ra_assists/src/handlers/auto_import.rs | 23 ++++++++-- crates/ra_hir/src/source_analyzer.rs | 60 ++++++++++++++++++++++++--- 2 files changed, 74 insertions(+), 9 deletions(-) (limited to 'crates') diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 1fb701da5..10984d8ad 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs @@ -46,9 +46,9 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option { let name_ref_to_import = path_under_caret.syntax().descendants().find_map(ast::NameRef::cast)?; - if source_analyzer - .resolve_path(ctx.db, &name_ref_to_import.syntax().ancestors().find_map(ast::Path::cast)?) - .is_some() + if dbg!(source_analyzer + .resolve_path(ctx.db, &name_ref_to_import.syntax().ancestors().find_map(ast::Path::cast)?)) + .is_some() { return None; } @@ -290,4 +290,21 @@ mod tests { ", ); } + + #[test] + fn not_applicable_for_imported_function() { + check_assist_not_applicable( + auto_import, + r" + pub mod test_mod { + pub fn test_function() {} + } + + use test_mod::test_function; + fn main() { + test_function<|> + } + ", + ); + } } diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index bb9a35c5d..49e1a10e0 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs @@ -20,7 +20,10 @@ use hir_def::{ use hir_expand::{ hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroCallKind, }; -use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment}; +use hir_ty::{ + method_resolution::{iterate_method_candidates, LookupMode}, + Canonical, InEnvironment, InferenceResult, TraitEnvironment, +}; use ra_syntax::{ ast::{self, AstNode}, AstPtr, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextUnit, @@ -28,8 +31,8 @@ use ra_syntax::{ use rustc_hash::FxHashSet; use crate::{ - db::HirDatabase, Adt, Const, DefWithBody, EnumVariant, Function, Local, MacroDef, Name, Path, - ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, + db::HirDatabase, Adt, AssocItem, Const, DefWithBody, EnumVariant, Function, Local, MacroDef, + ModuleDef, Name, Path, ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, }; /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of @@ -289,9 +292,11 @@ impl SourceAnalyzer { pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option { if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) { - let expr_id = self.expr_id(&path_expr.into())?; - if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) { - return Some(PathResolution::AssocItem(assoc.into())); + let path_resolution = self + .resolve_as_full_path(path_expr.clone()) + .or_else(|| self.resolve_as_path_to_method(db, &path_expr)); + if path_resolution.is_some() { + return path_resolution; } } if let Some(path_pat) = path.syntax().parent().and_then(ast::PathPat::cast) { @@ -305,6 +310,49 @@ impl SourceAnalyzer { self.resolve_hir_path(db, &hir_path) } + fn resolve_as_full_path(&self, path_expr: ast::PathExpr) -> Option { + let expr_id = self.expr_id(&path_expr.into())?; + self.infer + .as_ref()? + .assoc_resolutions_for_expr(expr_id) + .map(|assoc| PathResolution::AssocItem(assoc.into())) + } + + fn resolve_as_path_to_method( + &self, + db: &impl HirDatabase, + path_expr: &ast::PathExpr, + ) -> Option { + let full_path = path_expr.path()?; + let path_to_method = full_path.qualifier()?; + let method_name = full_path.segment()?.syntax().to_string(); + match self.resolve_path(db, &path_to_method)? { + PathResolution::Def(ModuleDef::Adt(adt)) => { + let ty = adt.ty(db); + iterate_method_candidates( + &Canonical { value: ty.ty.value, num_vars: 0 }, + db, + ty.ty.environment, + self.resolver.krate()?, + &self.resolver.traits_in_scope(db), + None, + LookupMode::Path, + |_, assoc_item_id| { + let assoc = assoc_item_id.into(); + if let AssocItem::Function(function) = assoc { + if function.name(db).to_string() == method_name { + return Some(assoc); + } + } + None + }, + ) + } + _ => None, + } + .map(PathResolution::AssocItem) + } + fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option { let name = name_ref.as_name(); let source_map = self.body_source_map.as_ref()?; -- cgit v1.2.3 From 2b9b16cb45c97424ea97a4959363cab4003f36e2 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 10 Feb 2020 00:30:00 +0200 Subject: Add method tests --- crates/ra_assists/src/handlers/auto_import.rs | 119 +++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 3 deletions(-) (limited to 'crates') diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 10984d8ad..27d96b941 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs @@ -46,9 +46,9 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option { let name_ref_to_import = path_under_caret.syntax().descendants().find_map(ast::NameRef::cast)?; - if dbg!(source_analyzer - .resolve_path(ctx.db, &name_ref_to_import.syntax().ancestors().find_map(ast::Path::cast)?)) - .is_some() + if source_analyzer + .resolve_path(ctx.db, &name_ref_to_import.syntax().ancestors().find_map(ast::Path::cast)?) + .is_some() { return None; } @@ -307,4 +307,117 @@ mod tests { ", ); } + + #[test] + fn associated_struct_function() { + check_assist( + auto_import, + r" + mod test_mod { + pub struct TestStruct {} + impl TestStruct { + pub fn test_function() {} + } + } + + fn main() { + TestStruct::test_function<|> + } + ", + r" + use test_mod::TestStruct; + + mod test_mod { + pub struct TestStruct {} + impl TestStruct { + pub fn test_function() {} + } + } + + fn main() { + TestStruct::test_function<|> + } + ", + ); + } + + #[test] + fn associated_trait_function() { + check_assist( + auto_import, + r" + mod test_mod { + pub trait TestTrait { + fn test_function(); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_function() {} + } + } + + fn main() { + test_mod::TestStruct::test_function<|> + } + ", + r" + use test_mod::TestTrait; + + mod test_mod { + pub trait TestTrait { + fn test_function(); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_function() {} + } + } + + fn main() { + test_mod::TestStruct::test_function<|> + } + ", + ); + } + + #[test] + fn trait_method() { + check_assist( + auto_import, + r" + mod test_mod { + pub trait TestTrait { + fn test_method(&self); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_method(&self) {} + } + } + + fn main() { + let test_struct = test_mod::TestStruct {}; + test_struct.test_method<|> + } + ", + r" + use test_mod::TestTrait; + + mod test_mod { + pub trait TestTrait { + fn test_method(&self); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_method(&self) {} + } + } + + fn main() { + let test_struct = test_mod::TestStruct {}; + test_struct.test_method<|> + } + ", + ); + } } -- cgit v1.2.3 From 9b6db7bbd416ae233df21e34311cd6efad1f57f8 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 10 Feb 2020 16:55:20 +0200 Subject: Refactor path for imports extraction --- crates/ra_assists/src/handlers/auto_import.rs | 112 +++++++++++++++++++++----- 1 file changed, 92 insertions(+), 20 deletions(-) (limited to 'crates') diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 27d96b941..a25f0650d 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs @@ -1,10 +1,11 @@ -use ra_ide_db::imports_locator::ImportsLocator; +use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; use ra_syntax::ast::{self, AstNode}; use crate::{ assist_ctx::{Assist, AssistCtx}, insert_use_statement, AssistId, }; +use hir::{db::HirDatabase, Adt, ModPath, Module, ModuleDef, PathResolution, SourceAnalyzer}; use std::collections::BTreeSet; // Assist: auto_import @@ -44,29 +45,13 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option { let source_analyzer = ctx.source_analyzer(&position, None); let module_with_name_to_import = source_analyzer.module()?; - let name_ref_to_import = - path_under_caret.syntax().descendants().find_map(ast::NameRef::cast)?; - if source_analyzer - .resolve_path(ctx.db, &name_ref_to_import.syntax().ancestors().find_map(ast::Path::cast)?) - .is_some() - { - return None; - } - - let name_to_import = name_ref_to_import.syntax().to_string(); - let proposed_imports = ImportsLocator::new(ctx.db) - .find_imports(&name_to_import) - .into_iter() - .filter_map(|module_def| module_with_name_to_import.find_use_path(ctx.db, module_def)) - .filter(|use_path| !use_path.segments.is_empty()) - .take(20) - .collect::>(); - + let import_candidate = ImportCandidate::new(&path_under_caret, &source_analyzer, ctx.db)?; + let proposed_imports = import_candidate.search_for_imports(ctx.db, module_with_name_to_import); if proposed_imports.is_empty() { return None; } - let mut group = ctx.add_assist_group(format!("Import {}", name_to_import)); + let mut group = ctx.add_assist_group(format!("Import {}", import_candidate.get_search_query())); for import in proposed_imports { group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), |edit| { edit.target(path_under_caret.syntax().text_range()); @@ -81,6 +66,92 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option { group.finish() } +#[derive(Debug)] +// TODO kb rustdocs +enum ImportCandidate { + UnqualifiedName(ast::NameRef), + QualifierStart(ast::NameRef), + TraitFunction(Adt, ast::PathSegment), +} + +impl ImportCandidate { + // TODO kb refactor this mess + fn new( + path_under_caret: &ast::Path, + source_analyzer: &SourceAnalyzer, + db: &impl HirDatabase, + ) -> Option { + if source_analyzer.resolve_path(db, path_under_caret).is_some() { + return None; + } + + let segment = path_under_caret.segment()?; + if let Some(qualifier) = path_under_caret.qualifier() { + let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; + let qualifier_start_path = + qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; + if let Some(qualifier_start_resolution) = + source_analyzer.resolve_path(db, &qualifier_start_path) + { + let qualifier_resolution = if &qualifier_start_path == path_under_caret { + qualifier_start_resolution + } else { + source_analyzer.resolve_path(db, &qualifier)? + }; + if let PathResolution::Def(ModuleDef::Adt(function_callee)) = qualifier_resolution { + Some(ImportCandidate::TraitFunction(function_callee, segment)) + } else { + None + } + } else { + Some(ImportCandidate::QualifierStart(qualifier_start)) + } + } else { + if source_analyzer.resolve_path(db, path_under_caret).is_none() { + Some(ImportCandidate::UnqualifiedName( + segment.syntax().descendants().find_map(ast::NameRef::cast)?, + )) + } else { + None + } + } + } + + fn get_search_query(&self) -> String { + match self { + ImportCandidate::UnqualifiedName(name_ref) + | ImportCandidate::QualifierStart(name_ref) => name_ref.syntax().to_string(), + ImportCandidate::TraitFunction(_, trait_function) => { + trait_function.syntax().to_string() + } + } + } + + fn search_for_imports( + &self, + db: &RootDatabase, + module_with_name_to_import: Module, + ) -> BTreeSet { + ImportsLocator::new(db) + .find_imports(&self.get_search_query()) + .into_iter() + .filter_map(|module_def| match self { + ImportCandidate::TraitFunction(function_callee, _) => { + if let ModuleDef::Function(function) = module_def { + dbg!(function); + todo!() + } else { + None + } + } + _ => module_with_name_to_import.find_use_path(db, module_def), + }) + .filter(|use_path| !use_path.segments.is_empty()) + .take(20) + .collect::>() + } +} + #[cfg(test)] mod tests { use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; @@ -381,6 +452,7 @@ mod tests { } #[test] + #[ignore] // TODO kb fn trait_method() { check_assist( auto_import, -- cgit v1.2.3 From 8f959f20ee0fecd644054ffed334c378f9ae20f5 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 11 Feb 2020 15:21:12 +0200 Subject: Trait location draft --- crates/ra_assists/src/handlers/auto_import.rs | 129 ++++++++++++++++++++++++-- crates/ra_hir/src/code_model.rs | 18 ++-- 2 files changed, 127 insertions(+), 20 deletions(-) (limited to 'crates') 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::{ assist_ctx::{Assist, AssistCtx}, insert_use_statement, AssistId, }; -use hir::{db::HirDatabase, Adt, ModPath, Module, ModuleDef, PathResolution, SourceAnalyzer}; +use ast::{FnDefOwner, ModuleItem, ModuleItemOwner}; +use hir::{ + db::{DefDatabase, HirDatabase}, + Adt, AssocContainerId, Crate, Function, HasSource, InFile, ModPath, Module, ModuleDef, + PathResolution, SourceAnalyzer, SourceBinder, Trait, +}; +use rustc_hash::FxHashSet; use std::collections::BTreeSet; // Assist: auto_import @@ -135,21 +141,88 @@ impl ImportCandidate { ImportsLocator::new(db) .find_imports(&self.get_search_query()) .into_iter() - .filter_map(|module_def| match self { + .map(|module_def| match self { ImportCandidate::TraitFunction(function_callee, _) => { - if let ModuleDef::Function(function) = module_def { - dbg!(function); - todo!() - } else { - None + let mut applicable_traits = Vec::new(); + if let ModuleDef::Function(located_function) = module_def { + let trait_candidates = Self::get_trait_candidates( + db, + located_function, + module_with_name_to_import.krate(), + ) + .into_iter() + .map(|trait_candidate| trait_candidate.into()) + .collect(); + + function_callee.ty(db).iterate_path_candidates( + db, + module_with_name_to_import.krate(), + &trait_candidates, + None, + |_, assoc| { + if let AssocContainerId::TraitId(trait_id) = assoc.container(db) { + applicable_traits.push( + module_with_name_to_import + .find_use_path(db, ModuleDef::Trait(trait_id.into())), + ); + }; + None::<()> + }, + ); } + applicable_traits } - _ => module_with_name_to_import.find_use_path(db, module_def), + _ => vec![module_with_name_to_import.find_use_path(db, module_def)], }) + .flatten() + .filter_map(std::convert::identity) .filter(|use_path| !use_path.segments.is_empty()) .take(20) .collect::>() } + + fn get_trait_candidates( + db: &RootDatabase, + called_function: Function, + root_crate: Crate, + ) -> FxHashSet { + let mut source_binder = SourceBinder::new(db); + root_crate + .dependencies(db) + .into_iter() + .map(|dependency| db.crate_def_map(dependency.krate.into())) + .chain(std::iter::once(db.crate_def_map(root_crate.into()))) + .map(|crate_def_map| { + crate_def_map + .modules + .iter() + .filter_map(|(_, module_data)| module_data.declaration_source(db)) + .filter_map(|in_file_module| { + Some((in_file_module.file_id, in_file_module.value.item_list()?.items())) + }) + .map(|(file_id, item_list)| { + let mut if_file_trait_defs = Vec::new(); + for module_item in item_list { + if let ModuleItem::TraitDef(trait_def) = module_item { + if let Some(item_list) = trait_def.item_list() { + if item_list + .functions() + .any(|fn_def| fn_def == called_function.source(db).value) + { + if_file_trait_defs.push(InFile::new(file_id, trait_def)) + } + } + } + } + if_file_trait_defs + }) + .flatten() + .filter_map(|in_file_trait_def| source_binder.to_def(in_file_trait_def)) + .collect::>() + }) + .flatten() + .collect() + } } #[cfg(test)] @@ -452,7 +525,45 @@ mod tests { } #[test] - #[ignore] // TODO kb + fn not_applicable_for_imported_trait() { + check_assist_not_applicable( + auto_import, + r" + mod test_mod { + pub trait TestTrait { + fn test_method(&self); + fn test_function(); + } + + pub trait TestTrait2 { + fn test_method(&self); + fn test_function(); + } + pub enum TestEnum { + One, + Two, + } + + impl TestTrait2 for TestEnum { + fn test_method(&self) {} + fn test_function() {} + } + + impl TestTrait for TestEnum { + fn test_method(&self) {} + fn test_function() {} + } + } + + use test_mod::TestTrait2; + fn main() { + test_mod::TestEnum::test_function<|>; + } + ", + ) + } + + #[test] fn trait_method() { check_assist( 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!( ); pub use hir_def::{ - attr::Attrs, item_scope::ItemInNs, visibility::Visibility, AssocItemId, AssocItemLoc, + attr::Attrs, item_scope::ItemInNs, visibility::Visibility, AssocContainerId, AssocItemId, }; use rustc_hash::FxHashSet; @@ -696,16 +696,12 @@ impl AssocItem { AssocItem::TypeAlias(t) => t.module(db), } } - pub fn container(self, db: &impl DefDatabase) -> AssocItemContainer { - let container = match self { - AssocItem::Function(it) => it.id.lookup(db).container, - AssocItem::Const(it) => it.id.lookup(db).container, - AssocItem::TypeAlias(it) => it.id.lookup(db).container, - }; - match container { - AssocContainerId::TraitId(id) => AssocItemContainer::Trait(id.into()), - AssocContainerId::ImplId(id) => AssocItemContainer::ImplBlock(id.into()), - AssocContainerId::ContainerId(_) => panic!("invalid AssocItem"), + + pub fn container(self, db: &impl DefDatabase) -> AssocContainerId { + match self { + AssocItem::Function(f) => f.id.lookup(db).container, + AssocItem::Const(c) => c.id.lookup(db).container, + AssocItem::TypeAlias(t) => t.id.lookup(db).container, } } } -- cgit v1.2.3 From d5c3808545e26d246d75e0754e81de803f9e53e6 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 11 Feb 2020 18:24:43 +0200 Subject: Support trait method call autoimports --- crates/ra_assists/src/handlers/auto_import.rs | 306 +++++++++++++++++--------- crates/ra_hir/src/code_model.rs | 6 +- 2 files changed, 211 insertions(+), 101 deletions(-) (limited to 'crates') diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index a9778fab7..9a366414c 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs @@ -1,15 +1,17 @@ use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; -use ra_syntax::ast::{self, AstNode}; +use ra_syntax::{ + ast::{self, AstNode}, + SyntaxNode, +}; use crate::{ assist_ctx::{Assist, AssistCtx}, insert_use_statement, AssistId, }; -use ast::{FnDefOwner, ModuleItem, ModuleItemOwner}; use hir::{ db::{DefDatabase, HirDatabase}, - Adt, AssocContainerId, Crate, Function, HasSource, InFile, ModPath, Module, ModuleDef, - PathResolution, SourceAnalyzer, SourceBinder, Trait, + AssocContainerId, AssocItem, Crate, Function, ModPath, Module, ModuleDef, PathResolution, + SourceAnalyzer, Trait, Type, }; use rustc_hash::FxHashSet; use std::collections::BTreeSet; @@ -34,36 +36,28 @@ use std::collections::BTreeSet; // # pub mod std { pub mod collections { pub struct HashMap { } } } // ``` pub(crate) fn auto_import(ctx: AssistCtx) -> Option { - let path_under_caret: ast::Path = ctx.find_node_at_offset()?; - if path_under_caret.syntax().ancestors().find_map(ast::UseItem::cast).is_some() { - return None; - } - - let module = path_under_caret.syntax().ancestors().find_map(ast::Module::cast); - let position = match module.and_then(|it| it.item_list()) { - Some(item_list) => item_list.syntax().clone(), - None => { - let current_file = - path_under_caret.syntax().ancestors().find_map(ast::SourceFile::cast)?; - current_file.syntax().clone() - } + let auto_import_assets = if let Some(path_under_caret) = ctx.find_node_at_offset::() + { + AutoImportAssets::for_regular_path(path_under_caret, &ctx)? + } else { + AutoImportAssets::for_method_call(ctx.find_node_at_offset()?, &ctx)? }; - let source_analyzer = ctx.source_analyzer(&position, None); - let module_with_name_to_import = source_analyzer.module()?; - let import_candidate = ImportCandidate::new(&path_under_caret, &source_analyzer, ctx.db)?; - let proposed_imports = import_candidate.search_for_imports(ctx.db, module_with_name_to_import); + let proposed_imports = auto_import_assets + .search_for_imports(ctx.db, auto_import_assets.module_with_name_to_import); if proposed_imports.is_empty() { return None; } - let mut group = ctx.add_assist_group(format!("Import {}", import_candidate.get_search_query())); + let mut group = + // TODO kb create another method and add something about traits there + ctx.add_assist_group(format!("Import {}", auto_import_assets.get_search_query())); for import in proposed_imports { group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), |edit| { - edit.target(path_under_caret.syntax().text_range()); + edit.target(auto_import_assets.syntax_under_caret.text_range()); insert_use_statement( - &position, - path_under_caret.syntax(), + &auto_import_assets.syntax_under_caret, + &auto_import_assets.syntax_under_caret, &import, edit.text_edit_builder(), ); @@ -72,64 +66,55 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option { group.finish() } -#[derive(Debug)] -// TODO kb rustdocs -enum ImportCandidate { - UnqualifiedName(ast::NameRef), - QualifierStart(ast::NameRef), - TraitFunction(Adt, ast::PathSegment), +struct AutoImportAssets { + import_candidate: ImportCandidate, + module_with_name_to_import: Module, + syntax_under_caret: SyntaxNode, } -impl ImportCandidate { - // TODO kb refactor this mess - fn new( - path_under_caret: &ast::Path, - source_analyzer: &SourceAnalyzer, - db: &impl HirDatabase, - ) -> Option { - if source_analyzer.resolve_path(db, path_under_caret).is_some() { +impl AutoImportAssets { + fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistCtx) -> Option { + let syntax_under_caret = method_call.syntax().to_owned(); + let source_analyzer = ctx.source_analyzer(&syntax_under_caret, None); + let module_with_name_to_import = source_analyzer.module()?; + Some(Self { + import_candidate: ImportCandidate::for_method_call( + &method_call, + &source_analyzer, + ctx.db, + )?, + module_with_name_to_import, + syntax_under_caret, + }) + } + + fn for_regular_path(path_under_caret: ast::Path, ctx: &AssistCtx) -> Option { + let syntax_under_caret = path_under_caret.syntax().to_owned(); + if syntax_under_caret.ancestors().find_map(ast::UseItem::cast).is_some() { return None; } - let segment = path_under_caret.segment()?; - if let Some(qualifier) = path_under_caret.qualifier() { - let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; - let qualifier_start_path = - qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; - if let Some(qualifier_start_resolution) = - source_analyzer.resolve_path(db, &qualifier_start_path) - { - let qualifier_resolution = if &qualifier_start_path == path_under_caret { - qualifier_start_resolution - } else { - source_analyzer.resolve_path(db, &qualifier)? - }; - if let PathResolution::Def(ModuleDef::Adt(function_callee)) = qualifier_resolution { - Some(ImportCandidate::TraitFunction(function_callee, segment)) - } else { - None - } - } else { - Some(ImportCandidate::QualifierStart(qualifier_start)) - } - } else { - if source_analyzer.resolve_path(db, path_under_caret).is_none() { - Some(ImportCandidate::UnqualifiedName( - segment.syntax().descendants().find_map(ast::NameRef::cast)?, - )) - } else { - None - } - } + let source_analyzer = ctx.source_analyzer(&syntax_under_caret, None); + let module_with_name_to_import = source_analyzer.module()?; + Some(Self { + import_candidate: ImportCandidate::for_regular_path( + &path_under_caret, + &source_analyzer, + ctx.db, + )?, + module_with_name_to_import, + syntax_under_caret, + }) } fn get_search_query(&self) -> String { - match self { + match &self.import_candidate { ImportCandidate::UnqualifiedName(name_ref) | ImportCandidate::QualifierStart(name_ref) => name_ref.syntax().to_string(), ImportCandidate::TraitFunction(_, trait_function) => { trait_function.syntax().to_string() } + ImportCandidate::TraitMethod(_, trait_method) => trait_method.syntax().to_string(), } } @@ -141,7 +126,7 @@ impl ImportCandidate { ImportsLocator::new(db) .find_imports(&self.get_search_query()) .into_iter() - .map(|module_def| match self { + .map(|module_def| match &self.import_candidate { ImportCandidate::TraitFunction(function_callee, _) => { let mut applicable_traits = Vec::new(); if let ModuleDef::Function(located_function) = module_def { @@ -154,7 +139,7 @@ impl ImportCandidate { .map(|trait_candidate| trait_candidate.into()) .collect(); - function_callee.ty(db).iterate_path_candidates( + function_callee.iterate_path_candidates( db, module_with_name_to_import.krate(), &trait_candidates, @@ -172,6 +157,42 @@ impl ImportCandidate { } applicable_traits } + ImportCandidate::TraitMethod(function_callee, _) => { + let mut applicable_traits = Vec::new(); + if let ModuleDef::Function(located_function) = module_def { + let trait_candidates: FxHashSet<_> = Self::get_trait_candidates( + db, + located_function, + module_with_name_to_import.krate(), + ) + .into_iter() + .map(|trait_candidate| trait_candidate.into()) + .collect(); + + if !trait_candidates.is_empty() { + function_callee.iterate_method_candidates( + db, + module_with_name_to_import.krate(), + &trait_candidates, + None, + |_, funciton| { + if let AssocContainerId::TraitId(trait_id) = + funciton.container(db) + { + applicable_traits.push( + module_with_name_to_import.find_use_path( + db, + ModuleDef::Trait(trait_id.into()), + ), + ); + }; + None::<()> + }, + ); + } + } + applicable_traits + } _ => vec![module_with_name_to_import.find_use_path(db, module_def)], }) .flatten() @@ -186,7 +207,6 @@ impl ImportCandidate { called_function: Function, root_crate: Crate, ) -> FxHashSet { - let mut source_binder = SourceBinder::new(db); root_crate .dependencies(db) .into_iter() @@ -196,28 +216,22 @@ impl ImportCandidate { crate_def_map .modules .iter() - .filter_map(|(_, module_data)| module_data.declaration_source(db)) - .filter_map(|in_file_module| { - Some((in_file_module.file_id, in_file_module.value.item_list()?.items())) - }) - .map(|(file_id, item_list)| { - let mut if_file_trait_defs = Vec::new(); - for module_item in item_list { - if let ModuleItem::TraitDef(trait_def) = module_item { - if let Some(item_list) = trait_def.item_list() { - if item_list - .functions() - .any(|fn_def| fn_def == called_function.source(db).value) - { - if_file_trait_defs.push(InFile::new(file_id, trait_def)) - } + .map(|(_, module_data)| { + let mut traits = Vec::new(); + for module_def_id in module_data.scope.declarations() { + if let ModuleDef::Trait(trait_candidate) = module_def_id.into() { + if trait_candidate + .items(db) + .into_iter() + .any(|item| item == AssocItem::Function(called_function)) + { + traits.push(trait_candidate) } } } - if_file_trait_defs + traits }) .flatten() - .filter_map(|in_file_trait_def| source_binder.to_def(in_file_trait_def)) .collect::>() }) .flatten() @@ -225,6 +239,72 @@ impl ImportCandidate { } } +#[derive(Debug)] +// TODO kb rustdocs +enum ImportCandidate { + UnqualifiedName(ast::NameRef), + QualifierStart(ast::NameRef), + TraitFunction(Type, ast::PathSegment), + TraitMethod(Type, ast::NameRef), +} + +impl ImportCandidate { + fn for_method_call( + method_call: &ast::MethodCallExpr, + source_analyzer: &SourceAnalyzer, + db: &impl HirDatabase, + ) -> Option { + if source_analyzer.resolve_method_call(method_call).is_some() { + return None; + } + Some(Self::TraitMethod( + source_analyzer.type_of(db, &method_call.expr()?)?, + method_call.name_ref()?, + )) + } + + fn for_regular_path( + path_under_caret: &ast::Path, + source_analyzer: &SourceAnalyzer, + db: &impl HirDatabase, + ) -> Option { + if source_analyzer.resolve_path(db, path_under_caret).is_some() { + return None; + } + + let segment = path_under_caret.segment()?; + if let Some(qualifier) = path_under_caret.qualifier() { + let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; + let qualifier_start_path = + qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; + if let Some(qualifier_start_resolution) = + source_analyzer.resolve_path(db, &qualifier_start_path) + { + let qualifier_resolution = if &qualifier_start_path == path_under_caret { + qualifier_start_resolution + } else { + source_analyzer.resolve_path(db, &qualifier)? + }; + if let PathResolution::Def(ModuleDef::Adt(function_callee)) = qualifier_resolution { + Some(ImportCandidate::TraitFunction(function_callee.ty(db), segment)) + } else { + None + } + } else { + Some(ImportCandidate::QualifierStart(qualifier_start)) + } + } else { + if source_analyzer.resolve_path(db, path_under_caret).is_none() { + Some(ImportCandidate::UnqualifiedName( + segment.syntax().descendants().find_map(ast::NameRef::cast)?, + )) + } else { + None + } + } + } +} + #[cfg(test)] mod tests { use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; @@ -525,32 +605,25 @@ mod tests { } #[test] - fn not_applicable_for_imported_trait() { + fn not_applicable_for_imported_trait_for_function() { check_assist_not_applicable( auto_import, r" mod test_mod { pub trait TestTrait { - fn test_method(&self); fn test_function(); } - pub trait TestTrait2 { - fn test_method(&self); fn test_function(); } pub enum TestEnum { One, Two, } - impl TestTrait2 for TestEnum { - fn test_method(&self) {} fn test_function() {} } - impl TestTrait for TestEnum { - fn test_method(&self) {} fn test_function() {} } } @@ -580,7 +653,7 @@ mod tests { fn main() { let test_struct = test_mod::TestStruct {}; - test_struct.test_method<|> + test_struct.test_meth<|>od() } ", r" @@ -598,9 +671,42 @@ mod tests { fn main() { let test_struct = test_mod::TestStruct {}; - test_struct.test_method<|> + test_struct.test_meth<|>od() } ", ); } + + #[test] + fn not_applicable_for_imported_trait_for_method() { + check_assist_not_applicable( + auto_import, + r" + mod test_mod { + pub trait TestTrait { + fn test_method(&self); + } + pub trait TestTrait2 { + fn test_method(&self); + } + pub enum TestEnum { + One, + Two, + } + impl TestTrait2 for TestEnum { + fn test_method(&self) {} + } + impl TestTrait for TestEnum { + fn test_method(&self) {} + } + } + + use test_mod::TestTrait2; + fn main() { + let one = test_mod::TestEnum::One; + one.test<|>_method(); + } + ", + ) + } } diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 73158b8bd..140b3a87f 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -548,6 +548,10 @@ impl Function { let mut validator = ExprValidator::new(self.id, infer, sink); validator.validate_body(db); } + + pub fn container(self, db: &impl DefDatabase) -> AssocContainerId { + self.id.lookup(db).container + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -699,7 +703,7 @@ impl AssocItem { pub fn container(self, db: &impl DefDatabase) -> AssocContainerId { match self { - AssocItem::Function(f) => f.id.lookup(db).container, + AssocItem::Function(f) => f.container(db), AssocItem::Const(c) => c.id.lookup(db).container, AssocItem::TypeAlias(t) => t.id.lookup(db).container, } -- cgit v1.2.3 From 24f7028d3f21b578b424a2b95998c2b9568aea80 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Wed, 12 Feb 2020 13:16:05 +0200 Subject: Add profiling --- crates/ra_assists/src/handlers/auto_import.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'crates') diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 9a366414c..2dcea3766 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs @@ -13,6 +13,7 @@ use hir::{ AssocContainerId, AssocItem, Crate, Function, ModPath, Module, ModuleDef, PathResolution, SourceAnalyzer, Trait, Type, }; +use ra_prof::profile; use rustc_hash::FxHashSet; use std::collections::BTreeSet; @@ -123,6 +124,7 @@ impl AutoImportAssets { db: &RootDatabase, module_with_name_to_import: Module, ) -> BTreeSet { + let _p = profile("auto_import::search_for_imports"); ImportsLocator::new(db) .find_imports(&self.get_search_query()) .into_iter() @@ -207,6 +209,7 @@ impl AutoImportAssets { called_function: Function, root_crate: Crate, ) -> FxHashSet { + let _p = profile("auto_import::get_trait_candidates"); root_crate .dependencies(db) .into_iter() -- cgit v1.2.3 From 07058cbf9f16591b8c250ce59026117ddae71c61 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Wed, 12 Feb 2020 13:21:12 +0200 Subject: Adjust the assist group name --- crates/ra_assists/src/handlers/auto_import.rs | 34 ++++++++++++++++++++------- 1 file changed, 25 insertions(+), 9 deletions(-) (limited to 'crates') diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 2dcea3766..c564f5027 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs @@ -1,9 +1,3 @@ -use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; -use ra_syntax::{ - ast::{self, AstNode}, - SyntaxNode, -}; - use crate::{ assist_ctx::{Assist, AssistCtx}, insert_use_statement, AssistId, @@ -13,7 +7,12 @@ use hir::{ AssocContainerId, AssocItem, Crate, Function, ModPath, Module, ModuleDef, PathResolution, SourceAnalyzer, Trait, Type, }; +use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; use ra_prof::profile; +use ra_syntax::{ + ast::{self, AstNode}, + SyntaxNode, +}; use rustc_hash::FxHashSet; use std::collections::BTreeSet; @@ -50,9 +49,13 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option { return None; } - let mut group = - // TODO kb create another method and add something about traits there - ctx.add_assist_group(format!("Import {}", auto_import_assets.get_search_query())); + let assist_group_name = if proposed_imports.len() == 1 { + format!("Import `{}`", proposed_imports.iter().next().unwrap()) + } else { + auto_import_assets.get_import_group_message() + }; + + let mut group = ctx.add_assist_group(assist_group_name); for import in proposed_imports { group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), |edit| { edit.target(auto_import_assets.syntax_under_caret.text_range()); @@ -119,6 +122,19 @@ impl AutoImportAssets { } } + fn get_import_group_message(&self) -> String { + match &self.import_candidate { + ImportCandidate::UnqualifiedName(name_ref) + | ImportCandidate::QualifierStart(name_ref) => format!("Import {}", name_ref.syntax()), + ImportCandidate::TraitFunction(_, trait_function) => { + format!("Import a trait for function {}", trait_function.syntax()) + } + ImportCandidate::TraitMethod(_, trait_method) => { + format!("Import a trait for method {}", trait_method.syntax()) + } + } + } + fn search_for_imports( &self, db: &RootDatabase, -- cgit v1.2.3 From acf5f43639c489022b5ff3273fc6324dbf2ef017 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Wed, 12 Feb 2020 14:41:34 +0200 Subject: Refactor the code --- crates/ra_assists/src/handlers/auto_import.rs | 189 +++++++++++++------------- 1 file changed, 93 insertions(+), 96 deletions(-) (limited to 'crates') diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index c564f5027..86615d659 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs @@ -36,15 +36,8 @@ use std::collections::BTreeSet; // # pub mod std { pub mod collections { pub struct HashMap { } } } // ``` pub(crate) fn auto_import(ctx: AssistCtx) -> Option { - let auto_import_assets = if let Some(path_under_caret) = ctx.find_node_at_offset::() - { - AutoImportAssets::for_regular_path(path_under_caret, &ctx)? - } else { - AutoImportAssets::for_method_call(ctx.find_node_at_offset()?, &ctx)? - }; - - let proposed_imports = auto_import_assets - .search_for_imports(ctx.db, auto_import_assets.module_with_name_to_import); + let auto_import_assets = AutoImportAssets::new(&ctx)?; + let proposed_imports = auto_import_assets.search_for_imports(ctx.db); if proposed_imports.is_empty() { return None; } @@ -54,7 +47,6 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option { } else { auto_import_assets.get_import_group_message() }; - let mut group = ctx.add_assist_group(assist_group_name); for import in proposed_imports { group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), |edit| { @@ -77,6 +69,14 @@ struct AutoImportAssets { } impl AutoImportAssets { + fn new(ctx: &AssistCtx) -> Option { + if let Some(path_under_caret) = ctx.find_node_at_offset::() { + Self::for_regular_path(path_under_caret, &ctx) + } else { + Self::for_method_call(ctx.find_node_at_offset()?, &ctx) + } + } + fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistCtx) -> Option { let syntax_under_caret = method_call.syntax().to_owned(); let source_analyzer = ctx.source_analyzer(&syntax_under_caret, None); @@ -111,36 +111,33 @@ impl AutoImportAssets { }) } - fn get_search_query(&self) -> String { + fn get_search_query(&self) -> &str { match &self.import_candidate { - ImportCandidate::UnqualifiedName(name_ref) - | ImportCandidate::QualifierStart(name_ref) => name_ref.syntax().to_string(), - ImportCandidate::TraitFunction(_, trait_function) => { - trait_function.syntax().to_string() - } - ImportCandidate::TraitMethod(_, trait_method) => trait_method.syntax().to_string(), + ImportCandidate::UnqualifiedName(name) => name, + ImportCandidate::QualifierStart(qualifier_start) => qualifier_start, + ImportCandidate::TraitFunction(_, trait_function_name) => trait_function_name, + ImportCandidate::TraitMethod(_, trait_method_name) => trait_method_name, } } fn get_import_group_message(&self) -> String { match &self.import_candidate { - ImportCandidate::UnqualifiedName(name_ref) - | ImportCandidate::QualifierStart(name_ref) => format!("Import {}", name_ref.syntax()), - ImportCandidate::TraitFunction(_, trait_function) => { - format!("Import a trait for function {}", trait_function.syntax()) + ImportCandidate::UnqualifiedName(name) => format!("Import {}", name), + ImportCandidate::QualifierStart(qualifier_start) => { + format!("Import {}", qualifier_start) } - ImportCandidate::TraitMethod(_, trait_method) => { - format!("Import a trait for method {}", trait_method.syntax()) + ImportCandidate::TraitFunction(_, trait_function_name) => { + format!("Import a trait for function {}", trait_function_name) + } + ImportCandidate::TraitMethod(_, trait_method_name) => { + format!("Import a trait for method {}", trait_method_name) } } } - fn search_for_imports( - &self, - db: &RootDatabase, - module_with_name_to_import: Module, - ) -> BTreeSet { + fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet { let _p = profile("auto_import::search_for_imports"); + let current_crate = self.module_with_name_to_import.krate(); ImportsLocator::new(db) .find_imports(&self.get_search_query()) .into_iter() @@ -148,49 +145,46 @@ impl AutoImportAssets { ImportCandidate::TraitFunction(function_callee, _) => { let mut applicable_traits = Vec::new(); if let ModuleDef::Function(located_function) = module_def { - let trait_candidates = Self::get_trait_candidates( - db, - located_function, - module_with_name_to_import.krate(), - ) - .into_iter() - .map(|trait_candidate| trait_candidate.into()) - .collect(); - - function_callee.iterate_path_candidates( - db, - module_with_name_to_import.krate(), - &trait_candidates, - None, - |_, assoc| { - if let AssocContainerId::TraitId(trait_id) = assoc.container(db) { - applicable_traits.push( - module_with_name_to_import - .find_use_path(db, ModuleDef::Trait(trait_id.into())), - ); - }; - None::<()> - }, - ); + let trait_candidates: FxHashSet<_> = + Self::get_trait_candidates(db, located_function, current_crate) + .into_iter() + .map(|trait_candidate| trait_candidate.into()) + .collect(); + if !trait_candidates.is_empty() { + function_callee.iterate_path_candidates( + db, + current_crate, + &trait_candidates, + None, + |_, assoc| { + if let AssocContainerId::TraitId(trait_id) = assoc.container(db) + { + applicable_traits.push( + self.module_with_name_to_import.find_use_path( + db, + ModuleDef::Trait(trait_id.into()), + ), + ); + }; + None::<()> + }, + ); + }; } applicable_traits } ImportCandidate::TraitMethod(function_callee, _) => { let mut applicable_traits = Vec::new(); if let ModuleDef::Function(located_function) = module_def { - let trait_candidates: FxHashSet<_> = Self::get_trait_candidates( - db, - located_function, - module_with_name_to_import.krate(), - ) - .into_iter() - .map(|trait_candidate| trait_candidate.into()) - .collect(); - + let trait_candidates: FxHashSet<_> = + Self::get_trait_candidates(db, located_function, current_crate) + .into_iter() + .map(|trait_candidate| trait_candidate.into()) + .collect(); if !trait_candidates.is_empty() { function_callee.iterate_method_candidates( db, - module_with_name_to_import.krate(), + current_crate, &trait_candidates, None, |_, funciton| { @@ -198,7 +192,7 @@ impl AutoImportAssets { funciton.container(db) { applicable_traits.push( - module_with_name_to_import.find_use_path( + self.module_with_name_to_import.find_use_path( db, ModuleDef::Trait(trait_id.into()), ), @@ -211,7 +205,7 @@ impl AutoImportAssets { } applicable_traits } - _ => vec![module_with_name_to_import.find_use_path(db, module_def)], + _ => vec![self.module_with_name_to_import.find_use_path(db, module_def)], }) .flatten() .filter_map(std::convert::identity) @@ -235,22 +229,19 @@ impl AutoImportAssets { crate_def_map .modules .iter() - .map(|(_, module_data)| { - let mut traits = Vec::new(); - for module_def_id in module_data.scope.declarations() { - if let ModuleDef::Trait(trait_candidate) = module_def_id.into() { - if trait_candidate - .items(db) - .into_iter() - .any(|item| item == AssocItem::Function(called_function)) - { - traits.push(trait_candidate) - } - } + .map(|(_, module_data)| module_data.scope.declarations()) + .flatten() + .filter_map(|module_def_id| match module_def_id.into() { + ModuleDef::Trait(trait_candidate) + if trait_candidate + .items(db) + .into_iter() + .any(|item| item == AssocItem::Function(called_function)) => + { + Some(trait_candidate) } - traits + _ => None, }) - .flatten() .collect::>() }) .flatten() @@ -259,12 +250,20 @@ impl AutoImportAssets { } #[derive(Debug)] -// TODO kb rustdocs enum ImportCandidate { - UnqualifiedName(ast::NameRef), - QualifierStart(ast::NameRef), - TraitFunction(Type, ast::PathSegment), - TraitMethod(Type, ast::NameRef), + /// Simple name like 'HashMap' + UnqualifiedName(String), + /// First part of the qualified name. + /// For 'std::collections::HashMap', that will be 'std'. + QualifierStart(String), + /// A trait function that has no self parameter. + /// For 'test_mod::TestEnum::test_function', `Type` is the `test_mod::TestEnum` expression type + /// and `String`is the `test_function` + TraitFunction(Type, String), + /// A trait method with self parameter. + /// For 'test_enum.test_method()', `Type` is the `test_enum` expression type + /// and `String` is the `test_method` + TraitMethod(Type, String), } impl ImportCandidate { @@ -278,7 +277,7 @@ impl ImportCandidate { } Some(Self::TraitMethod( source_analyzer.type_of(db, &method_call.expr()?)?, - method_call.name_ref()?, + method_call.name_ref()?.syntax().to_string(), )) } @@ -299,36 +298,34 @@ impl ImportCandidate { if let Some(qualifier_start_resolution) = source_analyzer.resolve_path(db, &qualifier_start_path) { - let qualifier_resolution = if &qualifier_start_path == path_under_caret { + let qualifier_resolution = if qualifier_start_path == qualifier { qualifier_start_resolution } else { source_analyzer.resolve_path(db, &qualifier)? }; if let PathResolution::Def(ModuleDef::Adt(function_callee)) = qualifier_resolution { - Some(ImportCandidate::TraitFunction(function_callee.ty(db), segment)) + Some(ImportCandidate::TraitFunction( + function_callee.ty(db), + segment.syntax().to_string(), + )) } else { None } } else { - Some(ImportCandidate::QualifierStart(qualifier_start)) + Some(ImportCandidate::QualifierStart(qualifier_start.syntax().to_string())) } } else { - if source_analyzer.resolve_path(db, path_under_caret).is_none() { - Some(ImportCandidate::UnqualifiedName( - segment.syntax().descendants().find_map(ast::NameRef::cast)?, - )) - } else { - None - } + Some(ImportCandidate::UnqualifiedName( + segment.syntax().descendants().find_map(ast::NameRef::cast)?.syntax().to_string(), + )) } } } #[cfg(test)] mod tests { - use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; - use super::*; + use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; #[test] fn applicable_when_found_an_import() { -- cgit v1.2.3 From afc1d18ff3e060a4107d7a17623a5e84f6c6c86e Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Wed, 12 Feb 2020 17:18:29 +0200 Subject: Fix post-rebase issues --- crates/ra_assists/src/handlers/auto_import.rs | 27 +++++++++++++-------------- crates/ra_hir/src/code_model.rs | 23 ++++++++++++----------- 2 files changed, 25 insertions(+), 25 deletions(-) (limited to 'crates') diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 86615d659..950ec7d05 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs @@ -4,8 +4,8 @@ use crate::{ }; use hir::{ db::{DefDatabase, HirDatabase}, - AssocContainerId, AssocItem, Crate, Function, ModPath, Module, ModuleDef, PathResolution, - SourceAnalyzer, Trait, Type, + AsAssocItem, AssocItem, AssocItemContainer, Crate, Function, ModPath, Module, ModuleDef, + PathResolution, SourceAnalyzer, Trait, Type, }; use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; use ra_prof::profile; @@ -157,13 +157,12 @@ impl AutoImportAssets { &trait_candidates, None, |_, assoc| { - if let AssocContainerId::TraitId(trait_id) = assoc.container(db) + if let AssocItemContainer::Trait(appropriate_trait) = + assoc.container(db) { applicable_traits.push( - self.module_with_name_to_import.find_use_path( - db, - ModuleDef::Trait(trait_id.into()), - ), + self.module_with_name_to_import + .find_use_path(db, appropriate_trait.into()), ); }; None::<()> @@ -187,15 +186,15 @@ impl AutoImportAssets { current_crate, &trait_candidates, None, - |_, funciton| { - if let AssocContainerId::TraitId(trait_id) = - funciton.container(db) + |_, function| { + if let AssocItemContainer::Trait(appropriate_trait) = function + .as_assoc_item(db) + .expect("Function is an assoc item") + .container(db) { applicable_traits.push( - self.module_with_name_to_import.find_use_path( - db, - ModuleDef::Trait(trait_id.into()), - ), + self.module_with_name_to_import + .find_use_path(db, appropriate_trait.into()), ); }; None::<()> diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 140b3a87f..cf3b04511 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -123,7 +123,7 @@ impl_froms!( ); pub use hir_def::{ - attr::Attrs, item_scope::ItemInNs, visibility::Visibility, AssocContainerId, AssocItemId, + attr::Attrs, item_scope::ItemInNs, visibility::Visibility, AssocItemId, AssocItemLoc, }; use rustc_hash::FxHashSet; @@ -548,10 +548,6 @@ impl Function { let mut validator = ExprValidator::new(self.id, infer, sink); validator.validate_body(db); } - - pub fn container(self, db: &impl DefDatabase) -> AssocContainerId { - self.id.lookup(db).container - } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -701,11 +697,16 @@ impl AssocItem { } } - pub fn container(self, db: &impl DefDatabase) -> AssocContainerId { - match self { - AssocItem::Function(f) => f.container(db), - AssocItem::Const(c) => c.id.lookup(db).container, - AssocItem::TypeAlias(t) => t.id.lookup(db).container, + pub fn container(self, db: &impl DefDatabase) -> AssocItemContainer { + let container = match self { + AssocItem::Function(it) => it.id.lookup(db).container, + AssocItem::Const(it) => it.id.lookup(db).container, + AssocItem::TypeAlias(it) => it.id.lookup(db).container, + }; + match container { + AssocContainerId::TraitId(id) => AssocItemContainer::Trait(id.into()), + AssocContainerId::ImplId(id) => AssocItemContainer::ImplBlock(id.into()), + AssocContainerId::ContainerId(_) => panic!("invalid AssocItem"), } } } @@ -818,7 +819,7 @@ impl TypeParam { } } -// FIXME: rename to `ImplBlock` +// FIXME: rename from `ImplBlock` to `Impl` #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ImplBlock { pub(crate) id: ImplId, -- cgit v1.2.3 From f65daf23dfb14677d6b23557e4356aec6fdee065 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Wed, 12 Feb 2020 17:34:17 +0200 Subject: Revert source_analyzer changes --- crates/ra_hir/src/code_model.rs | 1 - crates/ra_hir/src/source_analyzer.rs | 60 ++++-------------------------------- 2 files changed, 6 insertions(+), 55 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index cf3b04511..a56b8ab04 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -696,7 +696,6 @@ impl AssocItem { AssocItem::TypeAlias(t) => t.module(db), } } - pub fn container(self, db: &impl DefDatabase) -> AssocItemContainer { let container = match self { AssocItem::Function(it) => it.id.lookup(db).container, diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index 49e1a10e0..bb9a35c5d 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs @@ -20,10 +20,7 @@ use hir_def::{ use hir_expand::{ hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroCallKind, }; -use hir_ty::{ - method_resolution::{iterate_method_candidates, LookupMode}, - Canonical, InEnvironment, InferenceResult, TraitEnvironment, -}; +use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment}; use ra_syntax::{ ast::{self, AstNode}, AstPtr, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextUnit, @@ -31,8 +28,8 @@ use ra_syntax::{ use rustc_hash::FxHashSet; use crate::{ - db::HirDatabase, Adt, AssocItem, Const, DefWithBody, EnumVariant, Function, Local, MacroDef, - ModuleDef, Name, Path, ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, + db::HirDatabase, Adt, Const, DefWithBody, EnumVariant, Function, Local, MacroDef, Name, Path, + ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, }; /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of @@ -292,11 +289,9 @@ impl SourceAnalyzer { pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option { if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) { - let path_resolution = self - .resolve_as_full_path(path_expr.clone()) - .or_else(|| self.resolve_as_path_to_method(db, &path_expr)); - if path_resolution.is_some() { - return path_resolution; + let expr_id = self.expr_id(&path_expr.into())?; + if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) { + return Some(PathResolution::AssocItem(assoc.into())); } } if let Some(path_pat) = path.syntax().parent().and_then(ast::PathPat::cast) { @@ -310,49 +305,6 @@ impl SourceAnalyzer { self.resolve_hir_path(db, &hir_path) } - fn resolve_as_full_path(&self, path_expr: ast::PathExpr) -> Option { - let expr_id = self.expr_id(&path_expr.into())?; - self.infer - .as_ref()? - .assoc_resolutions_for_expr(expr_id) - .map(|assoc| PathResolution::AssocItem(assoc.into())) - } - - fn resolve_as_path_to_method( - &self, - db: &impl HirDatabase, - path_expr: &ast::PathExpr, - ) -> Option { - let full_path = path_expr.path()?; - let path_to_method = full_path.qualifier()?; - let method_name = full_path.segment()?.syntax().to_string(); - match self.resolve_path(db, &path_to_method)? { - PathResolution::Def(ModuleDef::Adt(adt)) => { - let ty = adt.ty(db); - iterate_method_candidates( - &Canonical { value: ty.ty.value, num_vars: 0 }, - db, - ty.ty.environment, - self.resolver.krate()?, - &self.resolver.traits_in_scope(db), - None, - LookupMode::Path, - |_, assoc_item_id| { - let assoc = assoc_item_id.into(); - if let AssocItem::Function(function) = assoc { - if function.name(db).to_string() == method_name { - return Some(assoc); - } - } - None - }, - ) - } - _ => None, - } - .map(PathResolution::AssocItem) - } - fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option { let name = name_ref.as_name(); let source_map = self.body_source_map.as_ref()?; -- cgit v1.2.3 From 3ccf8b746ab2fd18d1f617a5236ac9851facf0fa Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Wed, 12 Feb 2020 18:52:29 +0200 Subject: Also consider associated constants --- crates/ra_assists/src/handlers/auto_import.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'crates') diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 950ec7d05..8d8fe4645 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs @@ -115,7 +115,7 @@ impl AutoImportAssets { match &self.import_candidate { ImportCandidate::UnqualifiedName(name) => name, ImportCandidate::QualifierStart(qualifier_start) => qualifier_start, - ImportCandidate::TraitFunction(_, trait_function_name) => trait_function_name, + ImportCandidate::TraitAssocItem(_, trait_function_name) => trait_function_name, ImportCandidate::TraitMethod(_, trait_method_name) => trait_method_name, } } @@ -126,8 +126,8 @@ impl AutoImportAssets { ImportCandidate::QualifierStart(qualifier_start) => { format!("Import {}", qualifier_start) } - ImportCandidate::TraitFunction(_, trait_function_name) => { - format!("Import a trait for function {}", trait_function_name) + ImportCandidate::TraitAssocItem(_, trait_function_name) => { + format!("Import a trait for item {}", trait_function_name) } ImportCandidate::TraitMethod(_, trait_method_name) => { format!("Import a trait for method {}", trait_method_name) @@ -142,7 +142,7 @@ impl AutoImportAssets { .find_imports(&self.get_search_query()) .into_iter() .map(|module_def| match &self.import_candidate { - ImportCandidate::TraitFunction(function_callee, _) => { + ImportCandidate::TraitAssocItem(function_callee, _) => { let mut applicable_traits = Vec::new(); if let ModuleDef::Function(located_function) = module_def { let trait_candidates: FxHashSet<_> = @@ -255,10 +255,10 @@ enum ImportCandidate { /// First part of the qualified name. /// For 'std::collections::HashMap', that will be 'std'. QualifierStart(String), - /// A trait function that has no self parameter. + /// A trait associated function (with no self parameter) or associated constant. /// For 'test_mod::TestEnum::test_function', `Type` is the `test_mod::TestEnum` expression type - /// and `String`is the `test_function` - TraitFunction(Type, String), + /// and `String` is the `test_function` + TraitAssocItem(Type, String), /// A trait method with self parameter. /// For 'test_enum.test_method()', `Type` is the `test_enum` expression type /// and `String` is the `test_method` @@ -303,7 +303,7 @@ impl ImportCandidate { source_analyzer.resolve_path(db, &qualifier)? }; if let PathResolution::Def(ModuleDef::Adt(function_callee)) = qualifier_resolution { - Some(ImportCandidate::TraitFunction( + Some(ImportCandidate::TraitAssocItem( function_callee.ty(db), segment.syntax().to_string(), )) -- cgit v1.2.3 From e008b0805479fa2383eaf6bc346bda4255f6b1ac Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Wed, 12 Feb 2020 22:38:19 +0200 Subject: Support associated consts --- crates/ra_assists/src/handlers/auto_import.rs | 151 +++++++++++++++++++++++--- 1 file changed, 133 insertions(+), 18 deletions(-) (limited to 'crates') diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 8d8fe4645..903f11e4f 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs @@ -4,8 +4,8 @@ use crate::{ }; use hir::{ db::{DefDatabase, HirDatabase}, - AsAssocItem, AssocItem, AssocItemContainer, Crate, Function, ModPath, Module, ModuleDef, - PathResolution, SourceAnalyzer, Trait, Type, + AsAssocItem, AssocItem, AssocItemContainer, Crate, ModPath, Module, ModuleDef, PathResolution, + SourceAnalyzer, Trait, Type, }; use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; use ra_prof::profile; @@ -115,7 +115,7 @@ impl AutoImportAssets { match &self.import_candidate { ImportCandidate::UnqualifiedName(name) => name, ImportCandidate::QualifierStart(qualifier_start) => qualifier_start, - ImportCandidate::TraitAssocItem(_, trait_function_name) => trait_function_name, + ImportCandidate::TraitAssocItem(_, trait_assoc_item_name) => trait_assoc_item_name, ImportCandidate::TraitMethod(_, trait_method_name) => trait_method_name, } } @@ -126,8 +126,8 @@ impl AutoImportAssets { ImportCandidate::QualifierStart(qualifier_start) => { format!("Import {}", qualifier_start) } - ImportCandidate::TraitAssocItem(_, trait_function_name) => { - format!("Import a trait for item {}", trait_function_name) + ImportCandidate::TraitAssocItem(_, trait_assoc_item_name) => { + format!("Import a trait for item {}", trait_assoc_item_name) } ImportCandidate::TraitMethod(_, trait_method_name) => { format!("Import a trait for method {}", trait_method_name) @@ -142,16 +142,24 @@ impl AutoImportAssets { .find_imports(&self.get_search_query()) .into_iter() .map(|module_def| match &self.import_candidate { - ImportCandidate::TraitAssocItem(function_callee, _) => { + ImportCandidate::TraitAssocItem(assoc_item_type, _) => { + let located_assoc_item = match module_def { + ModuleDef::Function(located_function) => { + Some(AssocItem::Function(located_function)) + } + ModuleDef::Const(located_const) => Some(AssocItem::Const(located_const)), + _ => None, + }; + let mut applicable_traits = Vec::new(); - if let ModuleDef::Function(located_function) = module_def { + if let Some(located_assoc_item) = located_assoc_item { let trait_candidates: FxHashSet<_> = - Self::get_trait_candidates(db, located_function, current_crate) + Self::get_trait_candidates(db, located_assoc_item, current_crate) .into_iter() .map(|trait_candidate| trait_candidate.into()) .collect(); if !trait_candidates.is_empty() { - function_callee.iterate_path_candidates( + assoc_item_type.iterate_path_candidates( db, current_crate, &trait_candidates, @@ -175,11 +183,14 @@ impl AutoImportAssets { ImportCandidate::TraitMethod(function_callee, _) => { let mut applicable_traits = Vec::new(); if let ModuleDef::Function(located_function) = module_def { - let trait_candidates: FxHashSet<_> = - Self::get_trait_candidates(db, located_function, current_crate) - .into_iter() - .map(|trait_candidate| trait_candidate.into()) - .collect(); + let trait_candidates: FxHashSet<_> = Self::get_trait_candidates( + db, + AssocItem::Function(located_function), + current_crate, + ) + .into_iter() + .map(|trait_candidate| trait_candidate.into()) + .collect(); if !trait_candidates.is_empty() { function_callee.iterate_method_candidates( db, @@ -215,7 +226,7 @@ impl AutoImportAssets { fn get_trait_candidates( db: &RootDatabase, - called_function: Function, + called_assoc_item: AssocItem, root_crate: Crate, ) -> FxHashSet { let _p = profile("auto_import::get_trait_candidates"); @@ -235,7 +246,7 @@ impl AutoImportAssets { if trait_candidate .items(db) .into_iter() - .any(|item| item == AssocItem::Function(called_function)) => + .any(|item| item == called_assoc_item) => { Some(trait_candidate) } @@ -302,9 +313,9 @@ impl ImportCandidate { } else { source_analyzer.resolve_path(db, &qualifier)? }; - if let PathResolution::Def(ModuleDef::Adt(function_callee)) = qualifier_resolution { + if let PathResolution::Def(ModuleDef::Adt(assoc_item_path)) = qualifier_resolution { Some(ImportCandidate::TraitAssocItem( - function_callee.ty(db), + assoc_item_path.ty(db), segment.syntax().to_string(), )) } else { @@ -580,6 +591,39 @@ mod tests { ); } + #[test] + fn associated_struct_const() { + check_assist( + auto_import, + r" + mod test_mod { + pub struct TestStruct {} + impl TestStruct { + const TEST_CONST: u8 = 42; + } + } + + fn main() { + TestStruct::TEST_CONST<|> + } + ", + r" + use test_mod::TestStruct; + + mod test_mod { + pub struct TestStruct {} + impl TestStruct { + const TEST_CONST: u8 = 42; + } + } + + fn main() { + TestStruct::TEST_CONST<|> + } + ", + ); + } + #[test] fn associated_trait_function() { check_assist( @@ -651,6 +695,77 @@ mod tests { ) } + #[test] + fn associated_trait_const() { + check_assist( + auto_import, + r" + mod test_mod { + pub trait TestTrait { + const TEST_CONST: u8; + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + const TEST_CONST: u8 = 42; + } + } + + fn main() { + test_mod::TestStruct::TEST_CONST<|> + } + ", + r" + use test_mod::TestTrait; + + mod test_mod { + pub trait TestTrait { + const TEST_CONST: u8; + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + const TEST_CONST: u8 = 42; + } + } + + fn main() { + test_mod::TestStruct::TEST_CONST<|> + } + ", + ); + } + + #[test] + fn not_applicable_for_imported_trait_for_const() { + check_assist_not_applicable( + auto_import, + r" + mod test_mod { + pub trait TestTrait { + const TEST_CONST: u8; + } + pub trait TestTrait2 { + const TEST_CONST: f64; + } + pub enum TestEnum { + One, + Two, + } + impl TestTrait2 for TestEnum { + const TEST_CONST: f64 = 42.0; + } + impl TestTrait for TestEnum { + const TEST_CONST: u8 = 42; + } + } + + use test_mod::TestTrait2; + fn main() { + test_mod::TestEnum::TEST_CONST<|>; + } + ", + ) + } + #[test] fn trait_method() { check_assist( -- cgit v1.2.3 From e4f4cd77a07918529cd274f3299a020cbc884974 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Wed, 12 Feb 2020 23:27:19 +0200 Subject: Do not collect all traits --- crates/ra_assists/src/handlers/auto_import.rs | 155 +++++++++----------------- 1 file changed, 53 insertions(+), 102 deletions(-) (limited to 'crates') diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 903f11e4f..c4aea2a06 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs @@ -3,8 +3,7 @@ use crate::{ insert_use_statement, AssistId, }; use hir::{ - db::{DefDatabase, HirDatabase}, - AsAssocItem, AssocItem, AssocItemContainer, Crate, ModPath, Module, ModuleDef, PathResolution, + db::HirDatabase, AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, SourceAnalyzer, Trait, Type, }; use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; @@ -141,121 +140,73 @@ impl AutoImportAssets { ImportsLocator::new(db) .find_imports(&self.get_search_query()) .into_iter() - .map(|module_def| match &self.import_candidate { + .filter_map(|module_def| match &self.import_candidate { ImportCandidate::TraitAssocItem(assoc_item_type, _) => { let located_assoc_item = match module_def { - ModuleDef::Function(located_function) => { - Some(AssocItem::Function(located_function)) - } - ModuleDef::Const(located_const) => Some(AssocItem::Const(located_const)), + ModuleDef::Function(located_function) => located_function + .as_assoc_item(db) + .map(|assoc| assoc.container(db)) + .and_then(Self::assoc_to_trait), + ModuleDef::Const(located_const) => located_const + .as_assoc_item(db) + .map(|assoc| assoc.container(db)) + .and_then(Self::assoc_to_trait), _ => None, - }; - - let mut applicable_traits = Vec::new(); - if let Some(located_assoc_item) = located_assoc_item { - let trait_candidates: FxHashSet<_> = - Self::get_trait_candidates(db, located_assoc_item, current_crate) - .into_iter() - .map(|trait_candidate| trait_candidate.into()) - .collect(); - if !trait_candidates.is_empty() { - assoc_item_type.iterate_path_candidates( - db, - current_crate, - &trait_candidates, - None, - |_, assoc| { - if let AssocItemContainer::Trait(appropriate_trait) = - assoc.container(db) - { - applicable_traits.push( - self.module_with_name_to_import - .find_use_path(db, appropriate_trait.into()), - ); - }; - None::<()> - }, - ); - }; - } - applicable_traits + }?; + + let mut trait_candidates = FxHashSet::default(); + trait_candidates.insert(located_assoc_item.into()); + + assoc_item_type + .iterate_path_candidates( + db, + current_crate, + &trait_candidates, + None, + |_, assoc| Self::assoc_to_trait(assoc.container(db)), + ) + .map(ModuleDef::from) } ImportCandidate::TraitMethod(function_callee, _) => { - let mut applicable_traits = Vec::new(); - if let ModuleDef::Function(located_function) = module_def { - let trait_candidates: FxHashSet<_> = Self::get_trait_candidates( + let located_assoc_item = + if let ModuleDef::Function(located_function) = module_def { + located_function + .as_assoc_item(db) + .map(|assoc| assoc.container(db)) + .and_then(Self::assoc_to_trait) + } else { + None + }?; + + let mut trait_candidates = FxHashSet::default(); + trait_candidates.insert(located_assoc_item.into()); + + function_callee + .iterate_method_candidates( db, - AssocItem::Function(located_function), current_crate, + &trait_candidates, + None, + |_, function| { + Self::assoc_to_trait(function.as_assoc_item(db)?.container(db)) + }, ) - .into_iter() - .map(|trait_candidate| trait_candidate.into()) - .collect(); - if !trait_candidates.is_empty() { - function_callee.iterate_method_candidates( - db, - current_crate, - &trait_candidates, - None, - |_, function| { - if let AssocItemContainer::Trait(appropriate_trait) = function - .as_assoc_item(db) - .expect("Function is an assoc item") - .container(db) - { - applicable_traits.push( - self.module_with_name_to_import - .find_use_path(db, appropriate_trait.into()), - ); - }; - None::<()> - }, - ); - } - } - applicable_traits + .map(ModuleDef::from) } - _ => vec![self.module_with_name_to_import.find_use_path(db, module_def)], + _ => Some(module_def), }) - .flatten() - .filter_map(std::convert::identity) + .filter_map(|module_def| self.module_with_name_to_import.find_use_path(db, module_def)) .filter(|use_path| !use_path.segments.is_empty()) .take(20) .collect::>() } - fn get_trait_candidates( - db: &RootDatabase, - called_assoc_item: AssocItem, - root_crate: Crate, - ) -> FxHashSet { - let _p = profile("auto_import::get_trait_candidates"); - root_crate - .dependencies(db) - .into_iter() - .map(|dependency| db.crate_def_map(dependency.krate.into())) - .chain(std::iter::once(db.crate_def_map(root_crate.into()))) - .map(|crate_def_map| { - crate_def_map - .modules - .iter() - .map(|(_, module_data)| module_data.scope.declarations()) - .flatten() - .filter_map(|module_def_id| match module_def_id.into() { - ModuleDef::Trait(trait_candidate) - if trait_candidate - .items(db) - .into_iter() - .any(|item| item == called_assoc_item) => - { - Some(trait_candidate) - } - _ => None, - }) - .collect::>() - }) - .flatten() - .collect() + fn assoc_to_trait(assoc: AssocItemContainer) -> Option { + if let AssocItemContainer::Trait(extracted_trait) = assoc { + Some(extracted_trait) + } else { + None + } } } -- cgit v1.2.3