From 739edfd5cf6d1f5ac1ec6dbc28fc7943b3936e24 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 15 Apr 2021 23:31:42 +0300 Subject: [PATCH] Exclude inherent traits from flyimports --- crates/hir/src/lib.rs | 12 ++++++++++ crates/hir_ty/src/lib.rs | 1 + crates/hir_ty/src/utils.rs | 2 +- .../src/completions/flyimport.rs | 23 +++++++++++++++++++ crates/ide_db/src/helpers/import_assets.rs | 22 +++++++++++++----- 5 files changed, 53 insertions(+), 7 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index eba46a05696..0acfa582a0b 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2066,6 +2066,18 @@ impl Type { self.ty.dyn_trait().map(Into::into) } + /// If a type can be represented as `dyn Trait`, returns all traits accessible via this type, + /// or an empty iterator otherwise. + pub fn applicable_inherent_traits<'a>( + &'a self, + db: &'a dyn HirDatabase, + ) -> impl Iterator + 'a { + self.autoderef(db) + .filter_map(|derefed_type| derefed_type.ty.dyn_trait()) + .flat_map(move |dyn_trait_id| hir_ty::all_super_traits(db.upcast(), dyn_trait_id)) + .map(Trait::from) + } + pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option> { self.ty.impl_trait_bounds(db).map(|it| { it.into_iter() diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index 113234fa48c..0505fa4aeaf 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs @@ -56,6 +56,7 @@ pub use mapping::{ to_foreign_def_id, to_placeholder_idx, }; pub use traits::TraitEnvironment; +pub use utils::all_super_traits; pub use walk::TypeWalk; pub use chalk_ir::{ diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs index 5f6cb052af7..2f04ee57ae0 100644 --- a/crates/hir_ty/src/utils.rs +++ b/crates/hir_ty/src/utils.rs @@ -78,7 +78,7 @@ fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef) -> Vec Vec { +pub fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec { // we need to take care a bit here to avoid infinite loops in case of cycles // (i.e. if we have `trait A: B; trait B: A;`) let mut result = vec![trait_]; diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs index 5e61ecb4dbf..8e211ae1ed4 100644 --- a/crates/ide_completion/src/completions/flyimport.rs +++ b/crates/ide_completion/src/completions/flyimport.rs @@ -1127,4 +1127,27 @@ impl Bar for Foo { expect![[r#""#]], ); } + + #[test] + fn no_inherent_candidates_proposed() { + check( + r#" +mod baz { + pub trait DefDatabase { + fn method1(&self); + } + pub trait HirDatabase: DefDatabase { + fn method2(&self); + } +} + +mod bar { + fn test(db: &dyn crate::baz::HirDatabase) { + db.metho$0 + } +} + "#, + expect![[r#""#]], + ); + } } diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index 8ce64836711..91d6a466510 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs @@ -436,6 +436,8 @@ fn trait_applicable_items( }) .collect(); + let related_dyn_traits = + trait_candidate.receiver_ty.applicable_inherent_traits(db).collect::>(); let mut located_imports = FxHashSet::default(); if trait_assoc_item { @@ -451,12 +453,16 @@ fn trait_applicable_items( return None; } } + let located_trait = assoc.containing_trait(db)?; + if related_dyn_traits.contains(&located_trait) { + return None; + } - let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); + let trait_item = ItemInNs::from(ModuleDef::from(located_trait)); let original_item = assoc_to_item(assoc); located_imports.insert(LocatedImport::new( - mod_path(item)?, - item, + mod_path(trait_item)?, + trait_item, original_item, mod_path(original_item), )); @@ -473,11 +479,15 @@ fn trait_applicable_items( |_, function| { let assoc = function.as_assoc_item(db)?; if required_assoc_items.contains(&assoc) { - let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); + let located_trait = assoc.containing_trait(db)?; + if related_dyn_traits.contains(&located_trait) { + return None; + } + let trait_item = ItemInNs::from(ModuleDef::from(located_trait)); let original_item = assoc_to_item(assoc); located_imports.insert(LocatedImport::new( - mod_path(item)?, - item, + mod_path(trait_item)?, + trait_item, original_item, mod_path(original_item), ));