diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index ae6ef76067..c97ea18a24 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -1053,4 +1053,13 @@ impl AssocItem { AssocItem::TypeAlias(t) => t.module(db), } } + + pub fn container(self, db: &impl DefDatabase) -> Container { + match self { + AssocItem::Function(f) => f.container(db), + AssocItem::Const(c) => c.container(db), + AssocItem::TypeAlias(t) => t.container(db), + } + .expect("AssocItem without container") + } } diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs index 52e1fbf294..9c261eda9f 100644 --- a/crates/ra_hir/src/generics.rs +++ b/crates/ra_hir/src/generics.rs @@ -77,9 +77,10 @@ impl GenericParams { let parent = match def { GenericDef::Function(it) => it.container(db).map(GenericDef::from), GenericDef::TypeAlias(it) => it.container(db).map(GenericDef::from), + GenericDef::Const(it) => it.container(db).map(GenericDef::from), GenericDef::EnumVariant(it) => Some(it.parent_enum(db).into()), GenericDef::Adt(_) | GenericDef::Trait(_) => None, - GenericDef::ImplBlock(_) | GenericDef::Const(_) => None, + GenericDef::ImplBlock(_) => None, }; let mut generics = GenericParams { def, diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 0008cb232d..0398806fd1 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -28,8 +28,8 @@ use crate::{ ids::LocationCtx, resolve::{ScopeDef, TypeNs, ValueNs}, ty::method_resolution::implements_trait, - Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId, MacroDef, Module, - Name, Path, Resolver, Static, Struct, Ty, + AssocItem, Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId, + MacroDef, Module, Name, Path, Resolver, Static, Struct, Ty, }; fn try_get_resolver_for_node( @@ -327,7 +327,7 @@ impl SourceAnalyzer { db: &impl HirDatabase, ty: Ty, name: Option<&Name>, - callback: impl FnMut(&Ty, Function) -> Option, + callback: impl FnMut(&Ty, AssocItem) -> Option, ) -> Option { // There should be no inference vars in types passed here // FIXME check that? @@ -337,6 +337,7 @@ impl SourceAnalyzer { db, &self.resolver, name, + crate::ty::method_resolution::LookupMode::MethodCall, callback, ) } diff --git a/crates/ra_hir/src/ty/infer/path.rs b/crates/ra_hir/src/ty/infer/path.rs index c58564b22f..1946bf6084 100644 --- a/crates/ra_hir/src/ty/infer/path.rs +++ b/crates/ra_hir/src/ty/infer/path.rs @@ -6,11 +6,9 @@ use super::{ExprOrPatId, InferenceContext, TraitRef}; use crate::{ db::HirDatabase, resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs}, - ty::{lower, traits::TraitEnvironment, Canonical}, - ty::{Substs, Ty, TypableDef, TypeWalk}, - AssocItem, HasGenericParams, Name, Namespace, Path, Trait, + ty::{method_resolution, Substs, Ty, TypableDef, TypeWalk}, + AssocItem, Container, HasGenericParams, Name, Namespace, Path, }; -use std::sync::Arc; impl<'a, D: HirDatabase> InferenceContext<'a, D> { pub(super) fn infer_path( @@ -184,91 +182,34 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { return None; } - self.find_inherent_assoc_candidate(ty.clone(), name, id) - .or_else(|| self.find_trait_assoc_candidate(ty.clone(), name, id)) - } - - fn find_inherent_assoc_candidate( - &mut self, - ty: Ty, - name: &Name, - id: ExprOrPatId, - ) -> Option<(ValueNs, Option)> { - let krate = self.resolver.krate()?; - - // Find impl - let item = ty.clone().iterate_impl_items(self.db, krate, |item| match item { - AssocItem::Function(func) => { - if *name == func.name(self.db) { - Some(AssocItem::Function(func)) - } else { - None - } - } - - AssocItem::Const(konst) => { - if konst.name(self.db).map_or(false, |n| n == *name) { - Some(AssocItem::Const(konst)) - } else { - None - } - } - AssocItem::TypeAlias(_) => None, - })?; - let def = match item { - AssocItem::Function(f) => ValueNs::Function(f), - AssocItem::Const(c) => ValueNs::Const(c), - AssocItem::TypeAlias(_) => unreachable!(), - }; - let substs = self.find_self_types(&def, ty); - - self.write_assoc_resolution(id, item); - Some((def, substs)) - } - - fn find_trait_assoc_candidate( - &mut self, - ty: Ty, - name: &Name, - id: ExprOrPatId, - ) -> Option<(ValueNs, Option)> { - let krate = self.resolver.krate()?; - let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone()); - let env = lower::trait_env(self.db, &self.resolver); - // if we have `T: Trait` in the param env, the trait doesn't need to be in scope - let traits_from_env = env - .trait_predicates_for_self_ty(&ty) - .map(|tr| tr.trait_) - .flat_map(|t| t.all_super_traits(self.db)); - let traits = traits_from_env.chain(self.resolver.traits_in_scope(self.db)); - - 'traits: for t in traits { - let data = t.trait_data(self.db); - let mut known_implemented = false; - for item in data.items() { - if let AssocItem::Function(f) = *item { - if f.name(self.db) == *name { - if !known_implemented { - let goal = generic_implements_goal( - self.db, - env.clone(), - t, - canonical_ty.value.clone(), - ); - if self.db.trait_solve(krate, goal).is_none() { - continue 'traits; - } - } - known_implemented = true; + method_resolution::iterate_method_candidates( + &canonical_ty.value, + self.db, + &self.resolver.clone(), + Some(name), + method_resolution::LookupMode::Path, + move |_ty, item| { + let def = match item { + AssocItem::Function(f) => ValueNs::Function(f), + AssocItem::Const(c) => ValueNs::Const(c), + AssocItem::TypeAlias(_) => unreachable!(), + }; + match item.container(self.db) { + Container::ImplBlock(_) => { + let substs = self.find_self_types(&def, ty.clone()); + self.write_assoc_resolution(id, item); + Some((def, substs)) + } + Container::Trait(t) => { // we're picking this method let trait_substs = Substs::build_for_def(self.db, t) .push(ty.clone()) .fill(std::iter::repeat_with(|| self.new_type_var())) .build(); - let substs = Substs::build_for_def(self.db, f) + let substs = Substs::build_for_def(self.db, item) .use_parent_substs(&trait_substs) .fill_with_params() .build(); @@ -277,14 +218,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { substs: trait_substs, })); - self.write_assoc_resolution(id, *item); - return Some((ValueNs::Function(f), Some(substs))); + self.write_assoc_resolution(id, item); + Some((def, Some(substs))) } } - } - } - - None + }, + ) } fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option { @@ -317,23 +256,3 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } } } - -// TODO remove duplication -/// This creates Substs for a trait with the given Self type and type variables -/// for all other parameters, to query Chalk with it. -fn generic_implements_goal( - db: &impl HirDatabase, - env: Arc, - trait_: Trait, - self_ty: Canonical, -) -> Canonical> { - let num_vars = self_ty.num_vars; - let substs = super::Substs::build_for_def(db, trait_) - .push(self_ty.value) - .fill_with_bound_vars(num_vars as u32) - .build(); - let num_vars = substs.len() - 1 + self_ty.num_vars; - let trait_ref = TraitRef { trait_, substs }; - let obligation = super::Obligation::Trait(trait_ref); - Canonical { num_vars, value: super::InEnvironment::new(env, obligation) } -} diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index eb69344f6a..ee0c7b00f3 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -166,7 +166,19 @@ pub(crate) fn lookup_method( name: &Name, resolver: &Resolver, ) -> Option<(Ty, Function)> { - iterate_method_candidates(ty, db, resolver, Some(name), |ty, f| Some((ty.clone(), f))) + iterate_method_candidates(ty, db, resolver, Some(name), LookupMode::MethodCall, |ty, f| { + if let AssocItem::Function(f) = f { + Some((ty.clone(), f)) + } else { + None + } + }) +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum LookupMode { + MethodCall, + Path, } // This would be nicer if it just returned an iterator, but that runs into @@ -176,7 +188,8 @@ pub(crate) fn iterate_method_candidates( db: &impl HirDatabase, resolver: &Resolver, name: Option<&Name>, - mut callback: impl FnMut(&Ty, Function) -> Option, + mode: LookupMode, + mut callback: impl FnMut(&Ty, AssocItem) -> Option, ) -> Option { // For method calls, rust first does any number of autoderef, and then one // autoref (i.e. when the method takes &self or &mut self). We just ignore @@ -188,13 +201,15 @@ pub(crate) fn iterate_method_candidates( // rustc does an autoderef and then autoref again). let krate = resolver.krate()?; + // TODO no autoderef in LookupMode::Path for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) { - if let Some(result) = iterate_inherent_methods(&derefed_ty, db, name, krate, &mut callback) + if let Some(result) = + iterate_inherent_methods(&derefed_ty, db, name, mode, krate, &mut callback) { return Some(result); } if let Some(result) = - iterate_trait_method_candidates(&derefed_ty, db, resolver, name, &mut callback) + iterate_trait_method_candidates(&derefed_ty, db, resolver, name, mode, &mut callback) { return Some(result); } @@ -207,7 +222,8 @@ fn iterate_trait_method_candidates( db: &impl HirDatabase, resolver: &Resolver, name: Option<&Name>, - mut callback: impl FnMut(&Ty, Function) -> Option, + mode: LookupMode, + mut callback: impl FnMut(&Ty, AssocItem) -> Option, ) -> Option { let krate = resolver.krate()?; // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that) @@ -231,21 +247,35 @@ fn iterate_trait_method_candidates( // trait, but if we find out it doesn't, we'll skip the rest of the // iteration let mut known_implemented = inherently_implemented; - for item in data.items() { - if let AssocItem::Function(m) = *item { - let data = m.data(db); - if name.map_or(true, |name| data.name() == name) && data.has_self_param() { - if !known_implemented { - let goal = generic_implements_goal(db, env.clone(), t, ty.clone()); - if db.trait_solve(krate, goal).is_none() { - continue 'traits; - } - } - known_implemented = true; - if let Some(result) = callback(&ty.value, m) { - return Some(result); + for &item in data.items() { + // TODO unify with the impl case + match item { + AssocItem::Function(m) => { + let data = m.data(db); + if !name.map_or(true, |name| data.name() == name) + || (!data.has_self_param() && mode != LookupMode::Path) + { + continue; } } + AssocItem::Const(c) => { + if !name.map_or(true, |name| Some(name) == c.name(db).as_ref()) + || (mode != LookupMode::Path) + { + continue; + } + } + _ => {} + }; + if !known_implemented { + let goal = generic_implements_goal(db, env.clone(), t, ty.clone()); + if db.trait_solve(krate, goal).is_none() { + continue 'traits; + } + } + known_implemented = true; + if let Some(result) = callback(&ty.value, item) { + return Some(result); } } } @@ -256,21 +286,35 @@ fn iterate_inherent_methods( ty: &Canonical, db: &impl HirDatabase, name: Option<&Name>, + mode: LookupMode, krate: Crate, - mut callback: impl FnMut(&Ty, Function) -> Option, + mut callback: impl FnMut(&Ty, AssocItem) -> Option, ) -> Option { for krate in def_crates(db, krate, &ty.value)? { let impls = db.impls_in_crate(krate); for impl_block in impls.lookup_impl_blocks(&ty.value) { for item in impl_block.items(db) { - if let AssocItem::Function(f) = item { - let data = f.data(db); - if name.map_or(true, |name| data.name() == name) && data.has_self_param() { - if let Some(result) = callback(&ty.value, f) { - return Some(result); + match item { + AssocItem::Function(f) => { + let data = f.data(db); + if !name.map_or(true, |name| data.name() == name) + || (!data.has_self_param() && mode != LookupMode::Path) + { + continue; } } + AssocItem::Const(c) => { + if !name.map_or(true, |name| Some(name) == c.name(db).as_ref()) + || (mode != LookupMode::Path) + { + continue; + } + } + _ => {} + } + if let Some(result) = callback(&ty.value, item) { + return Some(result); } } } diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index e071e4d4e8..bfef48b161 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -1841,8 +1841,8 @@ fn test() { [243; 254) 'Struct::FOO': u32 [264; 265) 'y': u32 [268; 277) 'Enum::BAR': u32 - [287; 288) 'z': {unknown} - [291; 304) 'TraitTest::ID': {unknown} + [287; 288) 'z': u32 + [291; 304) 'TraitTest::ID': u32 "### ); } diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs index b4df6ee2ad..7135f481d0 100644 --- a/crates/ra_ide_api/src/completion/complete_dot.rs +++ b/crates/ra_ide_api/src/completion/complete_dot.rs @@ -58,10 +58,12 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) { let mut seen_methods = FxHashSet::default(); - ctx.analyzer.iterate_method_candidates(ctx.db, receiver, None, |_ty, func| { - let data = func.data(ctx.db); - if data.has_self_param() && seen_methods.insert(data.name().clone()) { - acc.add_function(ctx, func); + ctx.analyzer.iterate_method_candidates(ctx.db, receiver, None, |_ty, item| { + if let hir::AssocItem::Function(func) = item { + let data = func.data(ctx.db); + if data.has_self_param() && seen_methods.insert(data.name().clone()) { + acc.add_function(ctx, func); + } } None::<()> });