diff options
author | Seivan Heidari <[email protected]> | 2019-11-21 00:11:41 +0000 |
---|---|---|
committer | Seivan Heidari <[email protected]> | 2019-11-21 00:11:41 +0000 |
commit | 358a1bcd708c622836723e5201b6de77cc9ff327 (patch) | |
tree | aeff9c96a6059fa2b02e7c87ec88753bc7993d8d /crates/ra_hir_def/src/generics.rs | |
parent | 1e2d090ab8a9bda18f148b894b7948eb05b976e6 (diff) | |
parent | 612a72fc4ea4376920f2a7da7b3c334227c1716c (diff) |
Merge branch 'master' of https://github.com/rust-analyzer/rust-analyzer into feature/themes
Diffstat (limited to 'crates/ra_hir_def/src/generics.rs')
-rw-r--r-- | crates/ra_hir_def/src/generics.rs | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/crates/ra_hir_def/src/generics.rs b/crates/ra_hir_def/src/generics.rs new file mode 100644 index 000000000..9e2e4c3cc --- /dev/null +++ b/crates/ra_hir_def/src/generics.rs | |||
@@ -0,0 +1,185 @@ | |||
1 | //! Many kinds of items or constructs can have generic parameters: functions, | ||
2 | //! structs, impls, traits, etc. This module provides a common HIR for these | ||
3 | //! generic parameters. See also the `Generics` type and the `generics_of` query | ||
4 | //! in rustc. | ||
5 | use std::sync::Arc; | ||
6 | |||
7 | use hir_expand::name::{self, AsName, Name}; | ||
8 | use ra_syntax::ast::{self, NameOwner, TypeBoundsOwner, TypeParamsOwner}; | ||
9 | |||
10 | use crate::{ | ||
11 | db::DefDatabase2, | ||
12 | type_ref::{TypeBound, TypeRef}, | ||
13 | AdtId, AstItemDef, ContainerId, GenericDefId, HasSource, Lookup, | ||
14 | }; | ||
15 | |||
16 | /// Data about a generic parameter (to a function, struct, impl, ...). | ||
17 | #[derive(Clone, PartialEq, Eq, Debug)] | ||
18 | pub struct GenericParam { | ||
19 | // FIXME: give generic params proper IDs | ||
20 | pub idx: u32, | ||
21 | pub name: Name, | ||
22 | pub default: Option<TypeRef>, | ||
23 | } | ||
24 | |||
25 | /// Data about the generic parameters of a function, struct, impl, etc. | ||
26 | #[derive(Clone, PartialEq, Eq, Debug)] | ||
27 | pub struct GenericParams { | ||
28 | pub parent_params: Option<Arc<GenericParams>>, | ||
29 | pub params: Vec<GenericParam>, | ||
30 | pub where_predicates: Vec<WherePredicate>, | ||
31 | } | ||
32 | |||
33 | /// A single predicate from a where clause, i.e. `where Type: Trait`. Combined | ||
34 | /// where clauses like `where T: Foo + Bar` are turned into multiple of these. | ||
35 | /// It might still result in multiple actual predicates though, because of | ||
36 | /// associated type bindings like `Iterator<Item = u32>`. | ||
37 | #[derive(Clone, PartialEq, Eq, Debug)] | ||
38 | pub struct WherePredicate { | ||
39 | pub type_ref: TypeRef, | ||
40 | pub bound: TypeBound, | ||
41 | } | ||
42 | |||
43 | impl GenericParams { | ||
44 | pub(crate) fn generic_params_query( | ||
45 | db: &impl DefDatabase2, | ||
46 | def: GenericDefId, | ||
47 | ) -> Arc<GenericParams> { | ||
48 | let parent_generics = parent_generic_def(db, def).map(|it| db.generic_params(it)); | ||
49 | Arc::new(GenericParams::new(db, def.into(), parent_generics)) | ||
50 | } | ||
51 | |||
52 | fn new( | ||
53 | db: &impl DefDatabase2, | ||
54 | def: GenericDefId, | ||
55 | parent_params: Option<Arc<GenericParams>>, | ||
56 | ) -> GenericParams { | ||
57 | let mut generics = | ||
58 | GenericParams { params: Vec::new(), parent_params, where_predicates: Vec::new() }; | ||
59 | let start = generics.parent_params.as_ref().map(|p| p.params.len()).unwrap_or(0) as u32; | ||
60 | // FIXME: add `: Sized` bound for everything except for `Self` in traits | ||
61 | match def { | ||
62 | GenericDefId::FunctionId(it) => generics.fill(&it.lookup(db).source(db).value, start), | ||
63 | GenericDefId::AdtId(AdtId::StructId(it)) => { | ||
64 | generics.fill(&it.0.source(db).value, start) | ||
65 | } | ||
66 | GenericDefId::AdtId(AdtId::UnionId(it)) => generics.fill(&it.0.source(db).value, start), | ||
67 | GenericDefId::AdtId(AdtId::EnumId(it)) => generics.fill(&it.source(db).value, start), | ||
68 | GenericDefId::TraitId(it) => { | ||
69 | // traits get the Self type as an implicit first type parameter | ||
70 | generics.params.push(GenericParam { | ||
71 | idx: start, | ||
72 | name: name::SELF_TYPE, | ||
73 | default: None, | ||
74 | }); | ||
75 | generics.fill(&it.source(db).value, start + 1); | ||
76 | // add super traits as bounds on Self | ||
77 | // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar | ||
78 | let self_param = TypeRef::Path(name::SELF_TYPE.into()); | ||
79 | generics.fill_bounds(&it.source(db).value, self_param); | ||
80 | } | ||
81 | GenericDefId::TypeAliasId(it) => generics.fill(&it.lookup(db).source(db).value, start), | ||
82 | // Note that we don't add `Self` here: in `impl`s, `Self` is not a | ||
83 | // type-parameter, but rather is a type-alias for impl's target | ||
84 | // type, so this is handled by the resolver. | ||
85 | GenericDefId::ImplId(it) => generics.fill(&it.source(db).value, start), | ||
86 | GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => {} | ||
87 | } | ||
88 | |||
89 | generics | ||
90 | } | ||
91 | |||
92 | fn fill(&mut self, node: &impl TypeParamsOwner, start: u32) { | ||
93 | if let Some(params) = node.type_param_list() { | ||
94 | self.fill_params(params, start) | ||
95 | } | ||
96 | if let Some(where_clause) = node.where_clause() { | ||
97 | self.fill_where_predicates(where_clause); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | fn fill_bounds(&mut self, node: &impl ast::TypeBoundsOwner, type_ref: TypeRef) { | ||
102 | for bound in | ||
103 | node.type_bound_list().iter().flat_map(|type_bound_list| type_bound_list.bounds()) | ||
104 | { | ||
105 | self.add_where_predicate_from_bound(bound, type_ref.clone()); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | fn fill_params(&mut self, params: ast::TypeParamList, start: u32) { | ||
110 | for (idx, type_param) in params.type_params().enumerate() { | ||
111 | let name = type_param.name().map_or_else(Name::missing, |it| it.as_name()); | ||
112 | // FIXME: Use `Path::from_src` | ||
113 | let default = type_param.default_type().map(TypeRef::from_ast); | ||
114 | let param = GenericParam { idx: idx as u32 + start, name: name.clone(), default }; | ||
115 | self.params.push(param); | ||
116 | |||
117 | let type_ref = TypeRef::Path(name.into()); | ||
118 | self.fill_bounds(&type_param, type_ref); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | fn fill_where_predicates(&mut self, where_clause: ast::WhereClause) { | ||
123 | for pred in where_clause.predicates() { | ||
124 | let type_ref = match pred.type_ref() { | ||
125 | Some(type_ref) => type_ref, | ||
126 | None => continue, | ||
127 | }; | ||
128 | let type_ref = TypeRef::from_ast(type_ref); | ||
129 | for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) { | ||
130 | self.add_where_predicate_from_bound(bound, type_ref.clone()); | ||
131 | } | ||
132 | } | ||
133 | } | ||
134 | |||
135 | fn add_where_predicate_from_bound(&mut self, bound: ast::TypeBound, type_ref: TypeRef) { | ||
136 | if bound.has_question_mark() { | ||
137 | // FIXME: remove this bound | ||
138 | return; | ||
139 | } | ||
140 | let bound = TypeBound::from_ast(bound); | ||
141 | self.where_predicates.push(WherePredicate { type_ref, bound }); | ||
142 | } | ||
143 | |||
144 | pub fn find_by_name(&self, name: &Name) -> Option<&GenericParam> { | ||
145 | self.params.iter().find(|p| &p.name == name) | ||
146 | } | ||
147 | |||
148 | pub fn count_parent_params(&self) -> usize { | ||
149 | self.parent_params.as_ref().map(|p| p.count_params_including_parent()).unwrap_or(0) | ||
150 | } | ||
151 | |||
152 | pub fn count_params_including_parent(&self) -> usize { | ||
153 | let parent_count = self.count_parent_params(); | ||
154 | parent_count + self.params.len() | ||
155 | } | ||
156 | |||
157 | fn for_each_param<'a>(&'a self, f: &mut impl FnMut(&'a GenericParam)) { | ||
158 | if let Some(parent) = &self.parent_params { | ||
159 | parent.for_each_param(f); | ||
160 | } | ||
161 | self.params.iter().for_each(f); | ||
162 | } | ||
163 | |||
164 | pub fn params_including_parent(&self) -> Vec<&GenericParam> { | ||
165 | let mut vec = Vec::with_capacity(self.count_params_including_parent()); | ||
166 | self.for_each_param(&mut |p| vec.push(p)); | ||
167 | vec | ||
168 | } | ||
169 | } | ||
170 | |||
171 | fn parent_generic_def(db: &impl DefDatabase2, def: GenericDefId) -> Option<GenericDefId> { | ||
172 | let container = match def { | ||
173 | GenericDefId::FunctionId(it) => it.lookup(db).container, | ||
174 | GenericDefId::TypeAliasId(it) => it.lookup(db).container, | ||
175 | GenericDefId::ConstId(it) => it.lookup(db).container, | ||
176 | GenericDefId::EnumVariantId(it) => return Some(it.parent.into()), | ||
177 | GenericDefId::AdtId(_) | GenericDefId::TraitId(_) | GenericDefId::ImplId(_) => return None, | ||
178 | }; | ||
179 | |||
180 | match container { | ||
181 | ContainerId::ImplId(it) => Some(it.into()), | ||
182 | ContainerId::TraitId(it) => Some(it.into()), | ||
183 | ContainerId::ModuleId(_) => None, | ||
184 | } | ||
185 | } | ||