From 82fe7b77a3b4f49540ae1fc319bdd38afd73c877 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 23 Feb 2019 22:59:01 +0100 Subject: Refactor associated method resolution a bit and make it work with generics --- crates/ra_hir/src/ty/infer.rs | 86 +++++++++++++--------- crates/ra_hir/src/ty/lower.rs | 67 ++++++++++------- .../tests__infer_associated_method_generics.snap | 8 +- crates/ra_hir/src/ty/tests.rs | 1 - 4 files changed, 98 insertions(+), 64 deletions(-) diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 6ee9080d3..13080b5aa 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -360,46 +360,66 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { // we might have resolved into a type for which // we may find some associated item starting at the // path.segment pointed to by `remaining_index´ - let resolved = + let mut resolved = if remaining_index.is_none() { def.take_values()? } else { def.take_types()? }; + let remaining_index = remaining_index.unwrap_or(path.segments.len()); + + // resolve intermediate segments + for segment in &path.segments[remaining_index..] { + let ty = match resolved { + Resolution::Def(def) => { + let typable: Option = def.into(); + let typable = typable?; + + let substs = + Ty::substs_from_path_segment(self.db, &self.resolver, segment, typable); + self.db.type_for_def(typable, Namespace::Types).apply_substs(substs) + } + Resolution::LocalBinding(_) => { + // can't have a local binding in an associated item path + return None; + } + Resolution::GenericParam(..) => { + // TODO associated item of generic param + return None; + } + Resolution::SelfType(_) => { + // TODO associated item of self type + return None; + } + }; + + // Attempt to find an impl_item for the type which has a name matching + // the current segment + log::debug!("looking for path segment: {:?}", segment); + let item = ty.iterate_impl_items(self.db, |item| match item { + crate::ImplItem::Method(func) => { + let sig = func.signature(self.db); + if segment.name == *sig.name() { + return Some(func); + } + None + } + + // TODO: Resolve associated const + crate::ImplItem::Const(_) => None, + + // TODO: Resolve associated types + crate::ImplItem::Type(_) => None, + })?; + resolved = Resolution::Def(item.into()); + } + match resolved { Resolution::Def(def) => { let typable: Option = def.into(); let typable = typable?; - if let Some(remaining_index) = remaining_index { - let ty = self.db.type_for_def(typable, Namespace::Types); - // TODO: Keep resolving the segments - // if we have more segments to process - let segment = &path.segments[remaining_index]; - - log::debug!("looking for path segment: {:?}", segment); - - // Attempt to find an impl_item for the type which has a name matching - // the current segment - let ty = ty.iterate_impl_items(self.db, |item| match item { - crate::ImplItem::Method(func) => { - let sig = func.signature(self.db); - if segment.name == *sig.name() { - return Some(func.ty(self.db)); - } - None - } - - // TODO: Resolve associated const - crate::ImplItem::Const(_) => None, - - // TODO: Resolve associated types - crate::ImplItem::Type(_) => None, - }); - ty - } else { - let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable); - let ty = self.db.type_for_def(typable, Namespace::Values).apply_substs(substs); - let ty = self.insert_type_vars(ty); - Some(ty) - } + let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable); + let ty = self.db.type_for_def(typable, Namespace::Values).apply_substs(substs); + let ty = self.insert_type_vars(ty); + Some(ty) } Resolution::LocalBinding(pat) => { let ty = self.type_of_pat.get(pat)?; diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index cc9e0fd40..63e13a30e 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -16,7 +16,7 @@ use crate::{ name::KnownName, nameres::Namespace, resolve::{Resolver, Resolution}, - path::GenericArg, + path::{ PathSegment, GenericArg}, generics::GenericParams, adt::VariantDef, }; @@ -112,36 +112,18 @@ impl Ty { ty.apply_substs(substs) } - /// Collect generic arguments from a path into a `Substs`. See also - /// `create_substs_for_ast_path` and `def_to_ty` in rustc. - pub(super) fn substs_from_path( + pub(super) fn substs_from_path_segment( db: &impl HirDatabase, resolver: &Resolver, - path: &Path, + segment: &PathSegment, resolved: TypableDef, ) -> Substs { let mut substs = Vec::new(); - let last = path.segments.last().expect("path should have at least one segment"); - let (def_generics, segment) = match resolved { - TypableDef::Function(func) => (func.generic_params(db), last), - TypableDef::Struct(s) => (s.generic_params(db), last), - TypableDef::Enum(e) => (e.generic_params(db), last), - TypableDef::EnumVariant(var) => { - // the generic args for an enum variant may be either specified - // on the segment referring to the enum, or on the segment - // referring to the variant. So `Option::::None` and - // `Option::None::` are both allowed (though the former is - // preferred). See also `def_ids_for_path_segments` in rustc. - let len = path.segments.len(); - let segment = if len >= 2 && path.segments[len - 2].args_and_bindings.is_some() { - // Option::::None - &path.segments[len - 2] - } else { - // Option::None:: - last - }; - (var.parent_enum(db).generic_params(db), segment) - } + let def_generics = match resolved { + TypableDef::Function(func) => func.generic_params(db), + TypableDef::Struct(s) => s.generic_params(db), + TypableDef::Enum(e) => e.generic_params(db), + TypableDef::EnumVariant(var) => var.parent_enum(db).generic_params(db), }; let parent_param_count = def_generics.count_parent_params(); substs.extend((0..parent_param_count).map(|_| Ty::Unknown)); @@ -166,6 +148,39 @@ impl Ty { assert_eq!(substs.len(), def_generics.count_params_including_parent()); Substs(substs.into()) } + + /// Collect generic arguments from a path into a `Substs`. See also + /// `create_substs_for_ast_path` and `def_to_ty` in rustc. + pub(super) fn substs_from_path( + db: &impl HirDatabase, + resolver: &Resolver, + path: &Path, + resolved: TypableDef, + ) -> Substs { + let last = path.segments.last().expect("path should have at least one segment"); + let segment = match resolved { + TypableDef::Function(_) => last, + TypableDef::Struct(_) => last, + TypableDef::Enum(_) => last, + TypableDef::EnumVariant(_) => { + // the generic args for an enum variant may be either specified + // on the segment referring to the enum, or on the segment + // referring to the variant. So `Option::::None` and + // `Option::None::` are both allowed (though the former is + // preferred). See also `def_ids_for_path_segments` in rustc. + let len = path.segments.len(); + let segment = if len >= 2 && path.segments[len - 2].args_and_bindings.is_some() { + // Option::::None + &path.segments[len - 2] + } else { + // Option::None:: + last + }; + segment + } + }; + Ty::substs_from_path_segment(db, resolver, segment, resolved) + } } /// Build the declared type of an item. This depends on the namespace; e.g. for diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_generics.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_generics.snap index fe5d6590e..44694dfdb 100644 --- a/crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_generics.snap +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_generics.snap @@ -1,5 +1,5 @@ --- -created: "2019-02-21T10:25:18.568887300Z" +created: "2019-02-23T21:58:35.844769207Z" creator: insta@0.6.3 source: crates/ra_hir/src/ty/tests.rs expression: "&result" @@ -9,8 +9,8 @@ expression: "&result" [92; 103) 'Gen { val }': Gen [98; 101) 'val': T [123; 155) '{ ...32); }': () -[133; 134) 'a': Gen<[unknown]> -[137; 146) 'Gen::make': fn make<[unknown]>(T) -> Gen -[137; 152) 'Gen::make(0u32)': Gen<[unknown]> +[133; 134) 'a': Gen +[137; 146) 'Gen::make': fn make(T) -> Gen +[137; 152) 'Gen::make(0u32)': Gen [147; 151) '0u32': u32 diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 77aeca669..d0da34677 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -719,7 +719,6 @@ fn test() { } #[test] -#[ignore] // FIXME: After https://github.com/rust-analyzer/rust-analyzer/pull/866 is merged fn infer_associated_method_generics() { check_inference( "infer_associated_method_generics", -- cgit v1.2.3