aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Diebold <[email protected]>2019-09-14 12:25:05 +0100
committerFlorian Diebold <[email protected]>2019-09-17 18:47:45 +0100
commit913ab1ec0ad10873134ca429c1496806a9261206 (patch)
tree877db00b9898b278d9c9213824f9472d640de360
parent16ee779483a1d96ccd2258b43c8477744773b314 (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.rs79
-rw-r--r--crates/ra_hir/src/ty/tests.rs6
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