diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 3e04ea8da9..3739e522a1 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2623,13 +2623,15 @@ impl Type { } pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator + 'a { + self.autoderef_(db).map(move |ty| self.derived(ty)) + } + + pub fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator + 'a { // There should be no inference vars in types passed here let canonical = hir_ty::replace_errors_with_variables(&self.ty); let environment = self.env.env.clone(); let ty = InEnvironment { goal: canonical, environment }; - autoderef(db, Some(self.krate), ty) - .map(|canonical| canonical.value) - .map(move |ty| self.derived(ty)) + autoderef(db, Some(self.krate), ty).map(|canonical| canonical.value) } // This would be nicer if it just returned an iterator, but that runs into @@ -2808,22 +2810,32 @@ impl Type { db: &'a dyn HirDatabase, ) -> impl Iterator + 'a { let _p = profile::span("applicable_inherent_traits"); - self.autoderef(db) - .filter_map(|derefed_type| derefed_type.ty.dyn_trait()) + self.autoderef_(db) + .filter_map(|ty| 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> { + pub fn env_traits<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator + 'a { + let _p = profile::span("env_traits"); + self.autoderef_(db) + .filter(|ty| matches!(ty.kind(&Interner), TyKind::Placeholder(_))) + .flat_map(|ty| { + self.env + .traits_in_scope_from_clauses(ty) + .flat_map(|t| hir_ty::all_super_traits(db.upcast(), t)) + }) + .map(Trait::from) + } + + pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option> { self.ty.impl_trait_bounds(db).map(|it| { - it.into_iter() - .filter_map(|pred| match pred.skip_binders() { - hir_ty::WhereClause::Implemented(trait_ref) => { - Some(Trait::from(trait_ref.hir_trait_id())) - } - _ => None, - }) - .collect() + it.into_iter().filter_map(|pred| match pred.skip_binders() { + hir_ty::WhereClause::Implemented(trait_ref) => { + Some(Trait::from(trait_ref.hir_trait_id())) + } + _ => None, + }) }) } diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index 3fbb6fea19..6af39828be 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs @@ -719,7 +719,7 @@ fn iterate_trait_method_candidates( let env_traits = match self_ty.value.kind(&Interner) { TyKind::Placeholder(_) => { // if we have `T: Trait` in the param env, the trait doesn't need to be in scope - env.traits_in_scope_from_clauses(&self_ty.value) + env.traits_in_scope_from_clauses(self_ty.value.clone()) .flat_map(|t| all_super_traits(db.upcast(), t)) .collect() } diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs index 4b89fd5a85..3876a9da26 100644 --- a/crates/hir_ty/src/traits.rs +++ b/crates/hir_ty/src/traits.rs @@ -54,17 +54,13 @@ impl TraitEnvironment { } } - pub(crate) fn traits_in_scope_from_clauses<'a>( + pub fn traits_in_scope_from_clauses<'a>( &'a self, - ty: &'a Ty, + ty: Ty, ) -> impl Iterator + 'a { - self.traits_from_clauses.iter().filter_map(move |(self_ty, trait_id)| { - if self_ty == ty { - Some(*trait_id) - } else { - None - } - }) + self.traits_from_clauses + .iter() + .filter_map(move |(self_ty, trait_id)| (*self_ty == ty).then(|| *trait_id)) } } diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs index 031c4af0de..55cdb3200e 100644 --- a/crates/ide/src/goto_type_definition.rs +++ b/crates/ide/src/goto_type_definition.rs @@ -75,7 +75,7 @@ pub(crate) fn goto_type_definition( } else if let Some(trait_) = t.as_dyn_trait() { push(trait_.into()); } else if let Some(traits) = t.as_impl_traits(db) { - traits.into_iter().for_each(|it| push(it.into())); + traits.for_each(|it| push(it.into())); } else if let Some(trait_) = t.as_associated_type_parent_trait(db) { push(trait_.into()); } diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index dd77dd3e4b..7d5cfaa937 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -337,7 +337,7 @@ fn walk_and_push_ty( } else if let Some(trait_) = t.as_dyn_trait() { push_new_def(trait_.into()); } else if let Some(traits) = t.as_impl_traits(db) { - traits.into_iter().for_each(|it| push_new_def(it.into())); + traits.for_each(|it| push_new_def(it.into())); } else if let Some(trait_) = t.as_associated_type_parent_trait(db) { push_new_def(trait_.into()); } diff --git a/crates/ide_assists/src/utils/suggest_name.rs b/crates/ide_assists/src/utils/suggest_name.rs index 74523234b0..2021db3aba 100644 --- a/crates/ide_assists/src/utils/suggest_name.rs +++ b/crates/ide_assists/src/utils/suggest_name.rs @@ -243,7 +243,7 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase) -> Option { } else if let Some(trait_) = ty.as_dyn_trait() { trait_name(&trait_, db)? } else if let Some(traits) = ty.as_impl_traits(db) { - let mut iter = traits.into_iter().filter_map(|t| trait_name(&t, db)); + let mut iter = traits.filter_map(|t| trait_name(&t, db)); let name = iter.next()?; if iter.next().is_some() { return None; diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs index 0476590142..b3b27f251f 100644 --- a/crates/ide_completion/src/completions/pattern.rs +++ b/crates/ide_completion/src/completions/pattern.rs @@ -1,5 +1,7 @@ //! Completes constants and paths in unqualified patterns. +use hir::db::DefDatabase; + use crate::{ context::{PatternContext, PatternRefutability}, CompletionContext, Completions, @@ -13,11 +15,12 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { } _ => return, }; + let single_variant_enum = |enum_: hir::Enum| ctx.db.enum_data(enum_.into()).variants.len() == 1; - if refutable { - if let Some(hir::Adt::Enum(e)) = - ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) - { + if let Some(hir::Adt::Enum(e)) = + ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) + { + if refutable || single_variant_enum(e) { super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| { acc.add_qualified_variant_pat(ctx, variant, path.clone()); acc.add_qualified_enum_variant(ctx, variant, path); @@ -28,20 +31,20 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { // FIXME: ideally, we should look at the type we are matching against and // suggest variants + auto-imports ctx.process_all_names(&mut |name, res| { - let add_resolution = match &res { + let add_resolution = match res { hir::ScopeDef::ModuleDef(def) => match def { hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => { - acc.add_struct_pat(ctx, *strukt, Some(name.clone())); + acc.add_struct_pat(ctx, strukt, Some(name.clone())); true } - hir::ModuleDef::Variant(variant) if refutable => { - acc.add_variant_pat(ctx, *variant, Some(name.clone())); + hir::ModuleDef::Variant(variant) + if refutable || single_variant_enum(variant.parent_enum(ctx.db)) => + { + acc.add_variant_pat(ctx, variant, Some(name.clone())); true } - hir::ModuleDef::Adt(hir::Adt::Enum(..)) - | hir::ModuleDef::Variant(..) - | hir::ModuleDef::Const(..) - | hir::ModuleDef::Module(..) => refutable, + hir::ModuleDef::Adt(hir::Adt::Enum(e)) => refutable || single_variant_enum(e), + hir::ModuleDef::Const(..) | hir::ModuleDef::Module(..) => refutable, _ => false, }, hir::ScopeDef::MacroDef(mac) => mac.is_fn_like(), diff --git a/crates/ide_completion/src/tests/flyimport.rs b/crates/ide_completion/src/tests/flyimport.rs index 23e5da463c..1f96e122f9 100644 --- a/crates/ide_completion/src/tests/flyimport.rs +++ b/crates/ide_completion/src/tests/flyimport.rs @@ -924,6 +924,44 @@ mod bar { "#, expect![[r#""#]], ); + check( + r#" +mod baz { + pub trait DefDatabase { + fn method1(&self); + } + pub trait HirDatabase: DefDatabase { + fn method2(&self); + } +} + +mod bar { + fn test(db: &impl crate::baz::HirDatabase) { + db.metho$0 + } +} +"#, + expect![[r#""#]], + ); + check( + r#" +mod baz { + pub trait DefDatabase { + fn method1(&self); + } + pub trait HirDatabase: DefDatabase { + fn method2(&self); + } +} + +mod bar { + fn test(db: T) { + db.metho$0 + } +} +"#, + expect![[r#""#]], + ); } #[test] diff --git a/crates/ide_completion/src/tests/pattern.rs b/crates/ide_completion/src/tests/pattern.rs index 81c45c64cc..99e20b6ed0 100644 --- a/crates/ide_completion/src/tests/pattern.rs +++ b/crates/ide_completion/src/tests/pattern.rs @@ -130,18 +130,24 @@ fn foo() { fn irrefutable() { check( r#" +enum SingleVariantEnum { + Variant +} +use SingleVariantEnum::Variant; fn foo() { let a$0 } "#, expect![[r##" kw mut - bn Record Record { field$1 }$0 + bn Record Record { field$1 }$0 st Record - bn Tuple Tuple($1)$0 + bn Tuple Tuple($1)$0 st Tuple + ev Variant + en SingleVariantEnum st Unit - ma makro!(…) #[macro_export] macro_rules! makro + ma makro!(…) #[macro_export] macro_rules! makro "##]], ); } diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index 9a8adf167c..ac0ccfa63f 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs @@ -454,8 +454,10 @@ fn trait_applicable_items( let db = sema.db; - let related_dyn_traits = - trait_candidate.receiver_ty.applicable_inherent_traits(db).collect::>(); + let inherent_traits = trait_candidate.receiver_ty.applicable_inherent_traits(db); + let env_traits = trait_candidate.receiver_ty.env_traits(db); + let related_traits = inherent_traits.chain(env_traits).collect::>(); + let mut required_assoc_items = FxHashSet::default(); let trait_candidates = items_locator::items_with_name( sema, @@ -467,7 +469,7 @@ fn trait_applicable_items( .filter_map(|input| item_as_assoc(db, input)) .filter_map(|assoc| { let assoc_item_trait = assoc.containing_trait(db)?; - if related_dyn_traits.contains(&assoc_item_trait) { + if related_traits.contains(&assoc_item_trait) { None } else { required_assoc_items.insert(assoc);