diff options
author | Florian Diebold <[email protected]> | 2019-09-14 12:25:05 +0100 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2019-09-17 18:47:45 +0100 |
commit | 913ab1ec0ad10873134ca429c1496806a9261206 (patch) | |
tree | 877db00b9898b278d9c9213824f9472d640de360 | |
parent | 16ee779483a1d96ccd2258b43c8477744773b314 (diff) |
Resolve assoc types on type parameters
E.g. `fn foo<T: Iterator>() -> T::Item`. It seems that rustc does this only for
type parameters and only based on their bounds, so we also only consider traits
from bounds.
-rw-r--r-- | crates/ra_hir/src/ty/lower.rs | 79 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 6 |
2 files changed, 61 insertions, 24 deletions
diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index 6ead3846a..62e4ed0f4 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs | |||
@@ -93,7 +93,7 @@ impl Ty { | |||
93 | None => return Ty::Unknown, | 93 | None => return Ty::Unknown, |
94 | }; | 94 | }; |
95 | 95 | ||
96 | let typable: TypableDef = match resolution { | 96 | let ty = match resolution { |
97 | TypeNs::Trait(trait_) => { | 97 | TypeNs::Trait(trait_) => { |
98 | let segment = match remaining_index { | 98 | let segment = match remaining_index { |
99 | None => path.segments.last().expect("resolved path has at least one element"), | 99 | None => path.segments.last().expect("resolved path has at least one element"), |
@@ -115,12 +115,12 @@ impl Ty { | |||
115 | }) | 115 | }) |
116 | } | 116 | } |
117 | None => { | 117 | None => { |
118 | // associated type not found | 118 | // associated type not found (FIXME: report error) |
119 | Ty::Unknown | 119 | Ty::Unknown |
120 | } | 120 | } |
121 | } | 121 | } |
122 | } else { | 122 | } else { |
123 | // FIXME more than one segment remaining, is this possible? | 123 | // FIXME report error (ambiguous associated type) |
124 | Ty::Unknown | 124 | Ty::Unknown |
125 | } | 125 | } |
126 | } else { | 126 | } else { |
@@ -128,34 +128,71 @@ impl Ty { | |||
128 | }; | 128 | }; |
129 | } | 129 | } |
130 | TypeNs::GenericParam(idx) => { | 130 | TypeNs::GenericParam(idx) => { |
131 | if remaining_index.is_some() { | 131 | // FIXME: maybe return name in resolution? |
132 | // e.g. T::Item | 132 | let name = match remaining_index { |
133 | return Ty::Unknown; | 133 | None => path |
134 | } | ||
135 | return Ty::Param { | ||
136 | idx, | ||
137 | // FIXME: maybe return name in resolution? | ||
138 | name: path | ||
139 | .as_ident() | 134 | .as_ident() |
140 | .expect("generic param should be single-segment path") | 135 | .expect("generic param should be single-segment path") |
141 | .clone(), | 136 | .clone(), |
137 | Some(idx) => path.segments[idx - 1].name.clone(), | ||
142 | }; | 138 | }; |
139 | Ty::Param { idx, name } | ||
143 | } | 140 | } |
144 | TypeNs::SelfType(impl_block) => { | 141 | TypeNs::SelfType(impl_block) => impl_block.target_ty(db), |
145 | if remaining_index.is_some() { | ||
146 | // e.g. Self::Item | ||
147 | return Ty::Unknown; | ||
148 | } | ||
149 | return impl_block.target_ty(db); | ||
150 | } | ||
151 | 142 | ||
152 | TypeNs::Adt(it) => it.into(), | 143 | TypeNs::Adt(it) => Ty::from_hir_path_inner(db, resolver, path, it.into()), |
153 | TypeNs::BuiltinType(it) => it.into(), | 144 | TypeNs::BuiltinType(it) => Ty::from_hir_path_inner(db, resolver, path, it.into()), |
154 | TypeNs::TypeAlias(it) => it.into(), | 145 | TypeNs::TypeAlias(it) => Ty::from_hir_path_inner(db, resolver, path, it.into()), |
155 | // FIXME: report error | 146 | // FIXME: report error |
156 | TypeNs::EnumVariant(_) => return Ty::Unknown, | 147 | TypeNs::EnumVariant(_) => return Ty::Unknown, |
157 | }; | 148 | }; |
158 | 149 | ||
150 | if let Some(remaining_index) = remaining_index { | ||
151 | // resolve unselected assoc types | ||
152 | if remaining_index == path.segments.len() - 1 { | ||
153 | let segment = &path.segments[remaining_index]; | ||
154 | Ty::select_associated_type(db, resolver, ty, segment) | ||
155 | } else { | ||
156 | // FIXME report error (ambiguous associated type) | ||
157 | Ty::Unknown | ||
158 | } | ||
159 | } else { | ||
160 | ty | ||
161 | } | ||
162 | } | ||
163 | |||
164 | fn select_associated_type( | ||
165 | db: &impl HirDatabase, | ||
166 | resolver: &Resolver, | ||
167 | self_ty: Ty, | ||
168 | segment: &PathSegment, | ||
169 | ) -> Ty { | ||
170 | let env = trait_env(db, resolver); | ||
171 | let traits_from_env = env.trait_predicates_for_self_ty(&self_ty).map(|tr| tr.trait_); | ||
172 | let traits = traits_from_env.flat_map(|t| t.all_super_traits(db)); | ||
173 | let mut result = Ty::Unknown; | ||
174 | for t in traits { | ||
175 | if let Some(associated_ty) = t.associated_type_by_name(db, &segment.name) { | ||
176 | let generics = t.generic_params(db); | ||
177 | let mut substs = Vec::new(); | ||
178 | substs.push(self_ty.clone()); | ||
179 | substs.extend( | ||
180 | iter::repeat(Ty::Unknown).take(generics.count_params_including_parent() - 1), | ||
181 | ); | ||
182 | // FIXME handle type parameters on the segment | ||
183 | result = Ty::Projection(ProjectionTy { associated_ty, parameters: substs.into() }); | ||
184 | break; | ||
185 | } | ||
186 | } | ||
187 | result | ||
188 | } | ||
189 | |||
190 | fn from_hir_path_inner( | ||
191 | db: &impl HirDatabase, | ||
192 | resolver: &Resolver, | ||
193 | path: &Path, | ||
194 | typable: TypableDef, | ||
195 | ) -> Ty { | ||
159 | let ty = db.type_for_def(typable, Namespace::Types); | 196 | let ty = db.type_for_def(typable, Namespace::Types); |
160 | let substs = Ty::substs_from_path(db, resolver, path, typable); | 197 | let substs = Ty::substs_from_path(db, resolver, path, typable); |
161 | ty.subst(&substs) | 198 | ty.subst(&substs) |
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index b5d3c0180..dc4e8683d 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -2732,9 +2732,9 @@ fn test() { | |||
2732 | [147; 148) 't': T | 2732 | [147; 148) 't': T |
2733 | [178; 180) '{}': () | 2733 | [178; 180) '{}': () |
2734 | [191; 236) '{ ...(S); }': () | 2734 | [191; 236) '{ ...(S); }': () |
2735 | [201; 202) 'x': {unknown} | 2735 | [201; 202) 'x': u32 |
2736 | [205; 209) 'foo1': fn foo1<S>(T) -> {unknown} | 2736 | [205; 209) 'foo1': fn foo1<S>(T) -> <T as Iterable>::Item |
2737 | [205; 212) 'foo1(S)': {unknown} | 2737 | [205; 212) 'foo1(S)': u32 |
2738 | [210; 211) 'S': S | 2738 | [210; 211) 'S': S |
2739 | [222; 223) 'y': u32 | 2739 | [222; 223) 'y': u32 |
2740 | [226; 230) 'foo2': fn foo2<S>(T) -> <T as Iterable>::Item | 2740 | [226; 230) 'foo2': fn foo2<S>(T) -> <T as Iterable>::Item |