aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_ty/src/utils.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-08-13 15:35:29 +0100
committerAleksey Kladov <[email protected]>2020-08-13 15:35:29 +0100
commit6a77ec7bbe6ddbf663dce9529d11d1bb56c5489a (patch)
treeb6e3d7e0109eb82bca75b5ebc35a7d723542b8dd /crates/hir_ty/src/utils.rs
parent50f8c1ebf23f634b68529603a917e3feeda457fa (diff)
Rename ra_hir_ty -> hir_ty
Diffstat (limited to 'crates/hir_ty/src/utils.rs')
-rw-r--r--crates/hir_ty/src/utils.rs257
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`).
3use std::sync::Arc;
4
5use hir_def::generics::WherePredicateTarget;
6use 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};
15use hir_expand::name::{name, Name};
16
17use crate::{db::HirDatabase, GenericPredicate, TraitRef};
18
19fn 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
46fn 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).
70pub(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>`.
94pub(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
113pub(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
124pub(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.
136pub(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
143pub(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)]
149pub(crate) struct Generics {
150 def: GenericDefId,
151 pub(crate) params: Arc<GenericParams>,
152 parent_generics: Option<Box<Generics>>,
153}
154
155impl 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
243fn 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}