diff options
author | Aleksey Kladov <[email protected]> | 2020-08-13 15:35:29 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2020-08-13 15:35:29 +0100 |
commit | 6a77ec7bbe6ddbf663dce9529d11d1bb56c5489a (patch) | |
tree | b6e3d7e0109eb82bca75b5ebc35a7d723542b8dd /crates/hir_ty/src/utils.rs | |
parent | 50f8c1ebf23f634b68529603a917e3feeda457fa (diff) |
Rename ra_hir_ty -> hir_ty
Diffstat (limited to 'crates/hir_ty/src/utils.rs')
-rw-r--r-- | crates/hir_ty/src/utils.rs | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs new file mode 100644 index 000000000..e3e244268 --- /dev/null +++ b/crates/hir_ty/src/utils.rs | |||
@@ -0,0 +1,257 @@ | |||
1 | //! Helper functions for working with def, which don't need to be a separate | ||
2 | //! query, but can't be computed directly from `*Data` (ie, which need a `db`). | ||
3 | use std::sync::Arc; | ||
4 | |||
5 | use hir_def::generics::WherePredicateTarget; | ||
6 | use hir_def::{ | ||
7 | adt::VariantData, | ||
8 | db::DefDatabase, | ||
9 | generics::{GenericParams, TypeParamData, TypeParamProvenance}, | ||
10 | path::Path, | ||
11 | resolver::{HasResolver, TypeNs}, | ||
12 | type_ref::TypeRef, | ||
13 | AssocContainerId, GenericDefId, Lookup, TraitId, TypeAliasId, TypeParamId, VariantId, | ||
14 | }; | ||
15 | use hir_expand::name::{name, Name}; | ||
16 | |||
17 | use crate::{db::HirDatabase, GenericPredicate, TraitRef}; | ||
18 | |||
19 | fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { | ||
20 | let resolver = trait_.resolver(db); | ||
21 | // returning the iterator directly doesn't easily work because of | ||
22 | // lifetime problems, but since there usually shouldn't be more than a | ||
23 | // few direct traits this should be fine (we could even use some kind of | ||
24 | // SmallVec if performance is a concern) | ||
25 | let generic_params = db.generic_params(trait_.into()); | ||
26 | let trait_self = generic_params.find_trait_self_param(); | ||
27 | generic_params | ||
28 | .where_predicates | ||
29 | .iter() | ||
30 | .filter_map(|pred| match &pred.target { | ||
31 | WherePredicateTarget::TypeRef(TypeRef::Path(p)) if p == &Path::from(name![Self]) => { | ||
32 | pred.bound.as_path() | ||
33 | } | ||
34 | WherePredicateTarget::TypeParam(local_id) if Some(*local_id) == trait_self => { | ||
35 | pred.bound.as_path() | ||
36 | } | ||
37 | _ => None, | ||
38 | }) | ||
39 | .filter_map(|path| match resolver.resolve_path_in_type_ns_fully(db, path.mod_path()) { | ||
40 | Some(TypeNs::TraitId(t)) => Some(t), | ||
41 | _ => None, | ||
42 | }) | ||
43 | .collect() | ||
44 | } | ||
45 | |||
46 | fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef) -> Vec<TraitRef> { | ||
47 | // returning the iterator directly doesn't easily work because of | ||
48 | // lifetime problems, but since there usually shouldn't be more than a | ||
49 | // few direct traits this should be fine (we could even use some kind of | ||
50 | // SmallVec if performance is a concern) | ||
51 | let generic_params = db.generic_params(trait_ref.trait_.into()); | ||
52 | let trait_self = match generic_params.find_trait_self_param() { | ||
53 | Some(p) => TypeParamId { parent: trait_ref.trait_.into(), local_id: p }, | ||
54 | None => return Vec::new(), | ||
55 | }; | ||
56 | db.generic_predicates_for_param(trait_self) | ||
57 | .iter() | ||
58 | .filter_map(|pred| { | ||
59 | pred.as_ref().filter_map(|pred| match pred { | ||
60 | GenericPredicate::Implemented(tr) => Some(tr.clone()), | ||
61 | _ => None, | ||
62 | }) | ||
63 | }) | ||
64 | .map(|pred| pred.subst(&trait_ref.substs)) | ||
65 | .collect() | ||
66 | } | ||
67 | |||
68 | /// Returns an iterator over the whole super trait hierarchy (including the | ||
69 | /// trait itself). | ||
70 | pub(super) fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { | ||
71 | // we need to take care a bit here to avoid infinite loops in case of cycles | ||
72 | // (i.e. if we have `trait A: B; trait B: A;`) | ||
73 | let mut result = vec![trait_]; | ||
74 | let mut i = 0; | ||
75 | while i < result.len() { | ||
76 | let t = result[i]; | ||
77 | // yeah this is quadratic, but trait hierarchies should be flat | ||
78 | // enough that this doesn't matter | ||
79 | for tt in direct_super_traits(db, t) { | ||
80 | if !result.contains(&tt) { | ||
81 | result.push(tt); | ||
82 | } | ||
83 | } | ||
84 | i += 1; | ||
85 | } | ||
86 | result | ||
87 | } | ||
88 | |||
89 | /// Given a trait ref (`Self: Trait`), builds all the implied trait refs for | ||
90 | /// super traits. The original trait ref will be included. So the difference to | ||
91 | /// `all_super_traits` is that we keep track of type parameters; for example if | ||
92 | /// we have `Self: Trait<u32, i32>` and `Trait<T, U>: OtherTrait<U>` we'll get | ||
93 | /// `Self: OtherTrait<i32>`. | ||
94 | pub(super) fn all_super_trait_refs(db: &dyn HirDatabase, trait_ref: TraitRef) -> Vec<TraitRef> { | ||
95 | // we need to take care a bit here to avoid infinite loops in case of cycles | ||
96 | // (i.e. if we have `trait A: B; trait B: A;`) | ||
97 | let mut result = vec![trait_ref]; | ||
98 | let mut i = 0; | ||
99 | while i < result.len() { | ||
100 | let t = &result[i]; | ||
101 | // yeah this is quadratic, but trait hierarchies should be flat | ||
102 | // enough that this doesn't matter | ||
103 | for tt in direct_super_trait_refs(db, t) { | ||
104 | if !result.iter().any(|tr| tr.trait_ == tt.trait_) { | ||
105 | result.push(tt); | ||
106 | } | ||
107 | } | ||
108 | i += 1; | ||
109 | } | ||
110 | result | ||
111 | } | ||
112 | |||
113 | pub(super) fn associated_type_by_name_including_super_traits( | ||
114 | db: &dyn HirDatabase, | ||
115 | trait_ref: TraitRef, | ||
116 | name: &Name, | ||
117 | ) -> Option<(TraitRef, TypeAliasId)> { | ||
118 | all_super_trait_refs(db, trait_ref).into_iter().find_map(|t| { | ||
119 | let assoc_type = db.trait_data(t.trait_).associated_type_by_name(name)?; | ||
120 | Some((t, assoc_type)) | ||
121 | }) | ||
122 | } | ||
123 | |||
124 | pub(super) fn variant_data(db: &dyn DefDatabase, var: VariantId) -> Arc<VariantData> { | ||
125 | match var { | ||
126 | VariantId::StructId(it) => db.struct_data(it).variant_data.clone(), | ||
127 | VariantId::UnionId(it) => db.union_data(it).variant_data.clone(), | ||
128 | VariantId::EnumVariantId(it) => { | ||
129 | db.enum_data(it.parent).variants[it.local_id].variant_data.clone() | ||
130 | } | ||
131 | } | ||
132 | } | ||
133 | |||
134 | /// Helper for mutating `Arc<[T]>` (i.e. `Arc::make_mut` for Arc slices). | ||
135 | /// The underlying values are cloned if there are other strong references. | ||
136 | pub(crate) fn make_mut_slice<T: Clone>(a: &mut Arc<[T]>) -> &mut [T] { | ||
137 | if Arc::get_mut(a).is_none() { | ||
138 | *a = a.iter().cloned().collect(); | ||
139 | } | ||
140 | Arc::get_mut(a).unwrap() | ||
141 | } | ||
142 | |||
143 | pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics { | ||
144 | let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def))); | ||
145 | Generics { def, params: db.generic_params(def), parent_generics } | ||
146 | } | ||
147 | |||
148 | #[derive(Debug)] | ||
149 | pub(crate) struct Generics { | ||
150 | def: GenericDefId, | ||
151 | pub(crate) params: Arc<GenericParams>, | ||
152 | parent_generics: Option<Box<Generics>>, | ||
153 | } | ||
154 | |||
155 | impl Generics { | ||
156 | pub(crate) fn iter<'a>( | ||
157 | &'a self, | ||
158 | ) -> impl Iterator<Item = (TypeParamId, &'a TypeParamData)> + 'a { | ||
159 | self.parent_generics | ||
160 | .as_ref() | ||
161 | .into_iter() | ||
162 | .flat_map(|it| { | ||
163 | it.params | ||
164 | .types | ||
165 | .iter() | ||
166 | .map(move |(local_id, p)| (TypeParamId { parent: it.def, local_id }, p)) | ||
167 | }) | ||
168 | .chain( | ||
169 | self.params | ||
170 | .types | ||
171 | .iter() | ||
172 | .map(move |(local_id, p)| (TypeParamId { parent: self.def, local_id }, p)), | ||
173 | ) | ||
174 | } | ||
175 | |||
176 | pub(crate) fn iter_parent<'a>( | ||
177 | &'a self, | ||
178 | ) -> impl Iterator<Item = (TypeParamId, &'a TypeParamData)> + 'a { | ||
179 | self.parent_generics.as_ref().into_iter().flat_map(|it| { | ||
180 | it.params | ||
181 | .types | ||
182 | .iter() | ||
183 | .map(move |(local_id, p)| (TypeParamId { parent: it.def, local_id }, p)) | ||
184 | }) | ||
185 | } | ||
186 | |||
187 | pub(crate) fn len(&self) -> usize { | ||
188 | self.len_split().0 | ||
189 | } | ||
190 | |||
191 | /// (total, parents, child) | ||
192 | pub(crate) fn len_split(&self) -> (usize, usize, usize) { | ||
193 | let parent = self.parent_generics.as_ref().map_or(0, |p| p.len()); | ||
194 | let child = self.params.types.len(); | ||
195 | (parent + child, parent, child) | ||
196 | } | ||
197 | |||
198 | /// (parent total, self param, type param list, impl trait) | ||
199 | pub(crate) fn provenance_split(&self) -> (usize, usize, usize, usize) { | ||
200 | let parent = self.parent_generics.as_ref().map_or(0, |p| p.len()); | ||
201 | let self_params = self | ||
202 | .params | ||
203 | .types | ||
204 | .iter() | ||
205 | .filter(|(_, p)| p.provenance == TypeParamProvenance::TraitSelf) | ||
206 | .count(); | ||
207 | let list_params = self | ||
208 | .params | ||
209 | .types | ||
210 | .iter() | ||
211 | .filter(|(_, p)| p.provenance == TypeParamProvenance::TypeParamList) | ||
212 | .count(); | ||
213 | let impl_trait_params = self | ||
214 | .params | ||
215 | .types | ||
216 | .iter() | ||
217 | .filter(|(_, p)| p.provenance == TypeParamProvenance::ArgumentImplTrait) | ||
218 | .count(); | ||
219 | (parent, self_params, list_params, impl_trait_params) | ||
220 | } | ||
221 | |||
222 | pub(crate) fn param_idx(&self, param: TypeParamId) -> Option<usize> { | ||
223 | Some(self.find_param(param)?.0) | ||
224 | } | ||
225 | |||
226 | fn find_param(&self, param: TypeParamId) -> Option<(usize, &TypeParamData)> { | ||
227 | if param.parent == self.def { | ||
228 | let (idx, (_local_id, data)) = self | ||
229 | .params | ||
230 | .types | ||
231 | .iter() | ||
232 | .enumerate() | ||
233 | .find(|(_, (idx, _))| *idx == param.local_id) | ||
234 | .unwrap(); | ||
235 | let (_total, parent_len, _child) = self.len_split(); | ||
236 | Some((parent_len + idx, data)) | ||
237 | } else { | ||
238 | self.parent_generics.as_ref().and_then(|g| g.find_param(param)) | ||
239 | } | ||
240 | } | ||
241 | } | ||
242 | |||
243 | fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<GenericDefId> { | ||
244 | let container = match def { | ||
245 | GenericDefId::FunctionId(it) => it.lookup(db).container, | ||
246 | GenericDefId::TypeAliasId(it) => it.lookup(db).container, | ||
247 | GenericDefId::ConstId(it) => it.lookup(db).container, | ||
248 | GenericDefId::EnumVariantId(it) => return Some(it.parent.into()), | ||
249 | GenericDefId::AdtId(_) | GenericDefId::TraitId(_) | GenericDefId::ImplId(_) => return None, | ||
250 | }; | ||
251 | |||
252 | match container { | ||
253 | AssocContainerId::ImplId(it) => Some(it.into()), | ||
254 | AssocContainerId::TraitId(it) => Some(it.into()), | ||
255 | AssocContainerId::ContainerId(_) => None, | ||
256 | } | ||
257 | } | ||