diff options
author | Florian Diebold <[email protected]> | 2019-02-23 21:59:01 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2019-02-23 22:00:02 +0000 |
commit | 82fe7b77a3b4f49540ae1fc319bdd38afd73c877 (patch) | |
tree | 574d01f1f7532574ed22610341b3ac79da59d93f | |
parent | 1eef9fbefe44e919f6ddc7ce1c44625ffde6be1c (diff) |
Refactor associated method resolution a bit and make it work with generics
-rw-r--r-- | crates/ra_hir/src/ty/infer.rs | 86 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/lower.rs | 67 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/snapshots/tests__infer_associated_method_generics.snap | 8 | ||||
-rw-r--r-- | 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> { | |||
360 | // we might have resolved into a type for which | 360 | // we might have resolved into a type for which |
361 | // we may find some associated item starting at the | 361 | // we may find some associated item starting at the |
362 | // path.segment pointed to by `remaining_index´ | 362 | // path.segment pointed to by `remaining_index´ |
363 | let resolved = | 363 | let mut resolved = |
364 | if remaining_index.is_none() { def.take_values()? } else { def.take_types()? }; | 364 | if remaining_index.is_none() { def.take_values()? } else { def.take_types()? }; |
365 | 365 | ||
366 | let remaining_index = remaining_index.unwrap_or(path.segments.len()); | ||
367 | |||
368 | // resolve intermediate segments | ||
369 | for segment in &path.segments[remaining_index..] { | ||
370 | let ty = match resolved { | ||
371 | Resolution::Def(def) => { | ||
372 | let typable: Option<TypableDef> = def.into(); | ||
373 | let typable = typable?; | ||
374 | |||
375 | let substs = | ||
376 | Ty::substs_from_path_segment(self.db, &self.resolver, segment, typable); | ||
377 | self.db.type_for_def(typable, Namespace::Types).apply_substs(substs) | ||
378 | } | ||
379 | Resolution::LocalBinding(_) => { | ||
380 | // can't have a local binding in an associated item path | ||
381 | return None; | ||
382 | } | ||
383 | Resolution::GenericParam(..) => { | ||
384 | // TODO associated item of generic param | ||
385 | return None; | ||
386 | } | ||
387 | Resolution::SelfType(_) => { | ||
388 | // TODO associated item of self type | ||
389 | return None; | ||
390 | } | ||
391 | }; | ||
392 | |||
393 | // Attempt to find an impl_item for the type which has a name matching | ||
394 | // the current segment | ||
395 | log::debug!("looking for path segment: {:?}", segment); | ||
396 | let item = ty.iterate_impl_items(self.db, |item| match item { | ||
397 | crate::ImplItem::Method(func) => { | ||
398 | let sig = func.signature(self.db); | ||
399 | if segment.name == *sig.name() { | ||
400 | return Some(func); | ||
401 | } | ||
402 | None | ||
403 | } | ||
404 | |||
405 | // TODO: Resolve associated const | ||
406 | crate::ImplItem::Const(_) => None, | ||
407 | |||
408 | // TODO: Resolve associated types | ||
409 | crate::ImplItem::Type(_) => None, | ||
410 | })?; | ||
411 | resolved = Resolution::Def(item.into()); | ||
412 | } | ||
413 | |||
366 | match resolved { | 414 | match resolved { |
367 | Resolution::Def(def) => { | 415 | Resolution::Def(def) => { |
368 | let typable: Option<TypableDef> = def.into(); | 416 | let typable: Option<TypableDef> = def.into(); |
369 | let typable = typable?; | 417 | let typable = typable?; |
370 | 418 | ||
371 | if let Some(remaining_index) = remaining_index { | 419 | let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable); |
372 | let ty = self.db.type_for_def(typable, Namespace::Types); | 420 | let ty = self.db.type_for_def(typable, Namespace::Values).apply_substs(substs); |
373 | // TODO: Keep resolving the segments | 421 | let ty = self.insert_type_vars(ty); |
374 | // if we have more segments to process | 422 | Some(ty) |
375 | let segment = &path.segments[remaining_index]; | ||
376 | |||
377 | log::debug!("looking for path segment: {:?}", segment); | ||
378 | |||
379 | // Attempt to find an impl_item for the type which has a name matching | ||
380 | // the current segment | ||
381 | let ty = ty.iterate_impl_items(self.db, |item| match item { | ||
382 | crate::ImplItem::Method(func) => { | ||
383 | let sig = func.signature(self.db); | ||
384 | if segment.name == *sig.name() { | ||
385 | return Some(func.ty(self.db)); | ||
386 | } | ||
387 | None | ||
388 | } | ||
389 | |||
390 | // TODO: Resolve associated const | ||
391 | crate::ImplItem::Const(_) => None, | ||
392 | |||
393 | // TODO: Resolve associated types | ||
394 | crate::ImplItem::Type(_) => None, | ||
395 | }); | ||
396 | ty | ||
397 | } else { | ||
398 | let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable); | ||
399 | let ty = self.db.type_for_def(typable, Namespace::Values).apply_substs(substs); | ||
400 | let ty = self.insert_type_vars(ty); | ||
401 | Some(ty) | ||
402 | } | ||
403 | } | 423 | } |
404 | Resolution::LocalBinding(pat) => { | 424 | Resolution::LocalBinding(pat) => { |
405 | let ty = self.type_of_pat.get(pat)?; | 425 | 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::{ | |||
16 | name::KnownName, | 16 | name::KnownName, |
17 | nameres::Namespace, | 17 | nameres::Namespace, |
18 | resolve::{Resolver, Resolution}, | 18 | resolve::{Resolver, Resolution}, |
19 | path::GenericArg, | 19 | path::{ PathSegment, GenericArg}, |
20 | generics::GenericParams, | 20 | generics::GenericParams, |
21 | adt::VariantDef, | 21 | adt::VariantDef, |
22 | }; | 22 | }; |
@@ -112,36 +112,18 @@ impl Ty { | |||
112 | ty.apply_substs(substs) | 112 | ty.apply_substs(substs) |
113 | } | 113 | } |
114 | 114 | ||
115 | /// Collect generic arguments from a path into a `Substs`. See also | 115 | pub(super) fn substs_from_path_segment( |
116 | /// `create_substs_for_ast_path` and `def_to_ty` in rustc. | ||
117 | pub(super) fn substs_from_path( | ||
118 | db: &impl HirDatabase, | 116 | db: &impl HirDatabase, |
119 | resolver: &Resolver, | 117 | resolver: &Resolver, |
120 | path: &Path, | 118 | segment: &PathSegment, |
121 | resolved: TypableDef, | 119 | resolved: TypableDef, |
122 | ) -> Substs { | 120 | ) -> Substs { |
123 | let mut substs = Vec::new(); | 121 | let mut substs = Vec::new(); |
124 | let last = path.segments.last().expect("path should have at least one segment"); | 122 | let def_generics = match resolved { |
125 | let (def_generics, segment) = match resolved { | 123 | TypableDef::Function(func) => func.generic_params(db), |
126 | TypableDef::Function(func) => (func.generic_params(db), last), | 124 | TypableDef::Struct(s) => s.generic_params(db), |
127 | TypableDef::Struct(s) => (s.generic_params(db), last), | 125 | TypableDef::Enum(e) => e.generic_params(db), |
128 | TypableDef::Enum(e) => (e.generic_params(db), last), | 126 | TypableDef::EnumVariant(var) => var.parent_enum(db).generic_params(db), |
129 | TypableDef::EnumVariant(var) => { | ||
130 | // the generic args for an enum variant may be either specified | ||
131 | // on the segment referring to the enum, or on the segment | ||
132 | // referring to the variant. So `Option::<T>::None` and | ||
133 | // `Option::None::<T>` are both allowed (though the former is | ||
134 | // preferred). See also `def_ids_for_path_segments` in rustc. | ||
135 | let len = path.segments.len(); | ||
136 | let segment = if len >= 2 && path.segments[len - 2].args_and_bindings.is_some() { | ||
137 | // Option::<T>::None | ||
138 | &path.segments[len - 2] | ||
139 | } else { | ||
140 | // Option::None::<T> | ||
141 | last | ||
142 | }; | ||
143 | (var.parent_enum(db).generic_params(db), segment) | ||
144 | } | ||
145 | }; | 127 | }; |
146 | let parent_param_count = def_generics.count_parent_params(); | 128 | let parent_param_count = def_generics.count_parent_params(); |
147 | substs.extend((0..parent_param_count).map(|_| Ty::Unknown)); | 129 | substs.extend((0..parent_param_count).map(|_| Ty::Unknown)); |
@@ -166,6 +148,39 @@ impl Ty { | |||
166 | assert_eq!(substs.len(), def_generics.count_params_including_parent()); | 148 | assert_eq!(substs.len(), def_generics.count_params_including_parent()); |
167 | Substs(substs.into()) | 149 | Substs(substs.into()) |
168 | } | 150 | } |
151 | |||
152 | /// Collect generic arguments from a path into a `Substs`. See also | ||
153 | /// `create_substs_for_ast_path` and `def_to_ty` in rustc. | ||
154 | pub(super) fn substs_from_path( | ||
155 | db: &impl HirDatabase, | ||
156 | resolver: &Resolver, | ||
157 | path: &Path, | ||
158 | resolved: TypableDef, | ||
159 | ) -> Substs { | ||
160 | let last = path.segments.last().expect("path should have at least one segment"); | ||
161 | let segment = match resolved { | ||
162 | TypableDef::Function(_) => last, | ||
163 | TypableDef::Struct(_) => last, | ||
164 | TypableDef::Enum(_) => last, | ||
165 | TypableDef::EnumVariant(_) => { | ||
166 | // the generic args for an enum variant may be either specified | ||
167 | // on the segment referring to the enum, or on the segment | ||
168 | // referring to the variant. So `Option::<T>::None` and | ||
169 | // `Option::None::<T>` are both allowed (though the former is | ||
170 | // preferred). See also `def_ids_for_path_segments` in rustc. | ||
171 | let len = path.segments.len(); | ||
172 | let segment = if len >= 2 && path.segments[len - 2].args_and_bindings.is_some() { | ||
173 | // Option::<T>::None | ||
174 | &path.segments[len - 2] | ||
175 | } else { | ||
176 | // Option::None::<T> | ||
177 | last | ||
178 | }; | ||
179 | segment | ||
180 | } | ||
181 | }; | ||
182 | Ty::substs_from_path_segment(db, resolver, segment, resolved) | ||
183 | } | ||
169 | } | 184 | } |
170 | 185 | ||
171 | /// Build the declared type of an item. This depends on the namespace; e.g. for | 186 | /// 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 @@ | |||
1 | --- | 1 | --- |
2 | created: "2019-02-21T10:25:18.568887300Z" | 2 | created: "2019-02-23T21:58:35.844769207Z" |
3 | creator: [email protected] | 3 | creator: [email protected] |
4 | source: crates/ra_hir/src/ty/tests.rs | 4 | source: crates/ra_hir/src/ty/tests.rs |
5 | expression: "&result" | 5 | expression: "&result" |
@@ -9,8 +9,8 @@ expression: "&result" | |||
9 | [92; 103) 'Gen { val }': Gen<T> | 9 | [92; 103) 'Gen { val }': Gen<T> |
10 | [98; 101) 'val': T | 10 | [98; 101) 'val': T |
11 | [123; 155) '{ ...32); }': () | 11 | [123; 155) '{ ...32); }': () |
12 | [133; 134) 'a': Gen<[unknown]> | 12 | [133; 134) 'a': Gen<u32> |
13 | [137; 146) 'Gen::make': fn make<[unknown]>(T) -> Gen<T> | 13 | [137; 146) 'Gen::make': fn make<u32>(T) -> Gen<T> |
14 | [137; 152) 'Gen::make(0u32)': Gen<[unknown]> | 14 | [137; 152) 'Gen::make(0u32)': Gen<u32> |
15 | [147; 151) '0u32': u32 | 15 | [147; 151) '0u32': u32 |
16 | 16 | ||
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() { | |||
719 | } | 719 | } |
720 | 720 | ||
721 | #[test] | 721 | #[test] |
722 | #[ignore] // FIXME: After https://github.com/rust-analyzer/rust-analyzer/pull/866 is merged | ||
723 | fn infer_associated_method_generics() { | 722 | fn infer_associated_method_generics() { |
724 | check_inference( | 723 | check_inference( |
725 | "infer_associated_method_generics", | 724 | "infer_associated_method_generics", |