diff options
Diffstat (limited to 'crates/ra_hir_def/src')
-rw-r--r-- | crates/ra_hir_def/src/generics.rs | 163 | ||||
-rw-r--r-- | crates/ra_hir_def/src/lib.rs | 24 |
2 files changed, 187 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..4adfc16bb --- /dev/null +++ b/crates/ra_hir_def/src/generics.rs | |||
@@ -0,0 +1,163 @@ | |||
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 | |||
9 | use ra_syntax::ast::{self, NameOwner, TypeBoundsOwner, TypeParamsOwner}; | ||
10 | |||
11 | use crate::{ | ||
12 | db::DefDatabase2, | ||
13 | type_ref::{TypeBound, TypeRef}, | ||
14 | AdtId, AstItemDef, GenericDefId, | ||
15 | }; | ||
16 | |||
17 | /// Data about a generic parameter (to a function, struct, impl, ...). | ||
18 | #[derive(Clone, PartialEq, Eq, Debug)] | ||
19 | pub struct GenericParam { | ||
20 | // FIXME: give generic params proper IDs | ||
21 | pub idx: u32, | ||
22 | pub name: Name, | ||
23 | pub default: Option<TypeRef>, | ||
24 | } | ||
25 | |||
26 | /// Data about the generic parameters of a function, struct, impl, etc. | ||
27 | #[derive(Clone, PartialEq, Eq, Debug)] | ||
28 | pub struct GenericParams { | ||
29 | pub def: GenericDefId, | ||
30 | pub parent_params: Option<Arc<GenericParams>>, | ||
31 | pub params: Vec<GenericParam>, | ||
32 | pub where_predicates: Vec<WherePredicate>, | ||
33 | } | ||
34 | |||
35 | /// A single predicate from a where clause, i.e. `where Type: Trait`. Combined | ||
36 | /// where clauses like `where T: Foo + Bar` are turned into multiple of these. | ||
37 | /// It might still result in multiple actual predicates though, because of | ||
38 | /// associated type bindings like `Iterator<Item = u32>`. | ||
39 | #[derive(Clone, PartialEq, Eq, Debug)] | ||
40 | pub struct WherePredicate { | ||
41 | pub type_ref: TypeRef, | ||
42 | pub bound: TypeBound, | ||
43 | } | ||
44 | |||
45 | impl GenericParams { | ||
46 | pub fn new( | ||
47 | db: &impl DefDatabase2, | ||
48 | def: GenericDefId, | ||
49 | parent_params: Option<Arc<GenericParams>>, | ||
50 | ) -> GenericParams { | ||
51 | let mut generics = | ||
52 | GenericParams { def, params: Vec::new(), parent_params, where_predicates: Vec::new() }; | ||
53 | let start = generics.parent_params.as_ref().map(|p| p.params.len()).unwrap_or(0) as u32; | ||
54 | // FIXME: add `: Sized` bound for everything except for `Self` in traits | ||
55 | match def { | ||
56 | GenericDefId::FunctionId(it) => generics.fill(&it.source(db).value, start), | ||
57 | GenericDefId::AdtId(AdtId::StructId(it)) => { | ||
58 | generics.fill(&it.0.source(db).value, start) | ||
59 | } | ||
60 | GenericDefId::AdtId(AdtId::UnionId(it)) => generics.fill(&it.0.source(db).value, start), | ||
61 | GenericDefId::AdtId(AdtId::EnumId(it)) => generics.fill(&it.source(db).value, start), | ||
62 | GenericDefId::TraitId(it) => { | ||
63 | // traits get the Self type as an implicit first type parameter | ||
64 | generics.params.push(GenericParam { | ||
65 | idx: start, | ||
66 | name: name::SELF_TYPE, | ||
67 | default: None, | ||
68 | }); | ||
69 | generics.fill(&it.source(db).value, start + 1); | ||
70 | // add super traits as bounds on Self | ||
71 | // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar | ||
72 | let self_param = TypeRef::Path(name::SELF_TYPE.into()); | ||
73 | generics.fill_bounds(&it.source(db).value, self_param); | ||
74 | } | ||
75 | GenericDefId::TypeAliasId(it) => generics.fill(&it.source(db).value, start), | ||
76 | // Note that we don't add `Self` here: in `impl`s, `Self` is not a | ||
77 | // type-parameter, but rather is a type-alias for impl's target | ||
78 | // type, so this is handled by the resolver. | ||
79 | GenericDefId::ImplId(it) => generics.fill(&it.source(db).value, start), | ||
80 | GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => {} | ||
81 | } | ||
82 | |||
83 | generics | ||
84 | } | ||
85 | |||
86 | fn fill(&mut self, node: &impl TypeParamsOwner, start: u32) { | ||
87 | if let Some(params) = node.type_param_list() { | ||
88 | self.fill_params(params, start) | ||
89 | } | ||
90 | if let Some(where_clause) = node.where_clause() { | ||
91 | self.fill_where_predicates(where_clause); | ||
92 | } | ||
93 | } | ||
94 | |||
95 | fn fill_bounds(&mut self, node: &impl ast::TypeBoundsOwner, type_ref: TypeRef) { | ||
96 | for bound in | ||
97 | node.type_bound_list().iter().flat_map(|type_bound_list| type_bound_list.bounds()) | ||
98 | { | ||
99 | self.add_where_predicate_from_bound(bound, type_ref.clone()); | ||
100 | } | ||
101 | } | ||
102 | |||
103 | fn fill_params(&mut self, params: ast::TypeParamList, start: u32) { | ||
104 | for (idx, type_param) in params.type_params().enumerate() { | ||
105 | let name = type_param.name().map_or_else(Name::missing, |it| it.as_name()); | ||
106 | // FIXME: Use `Path::from_src` | ||
107 | let default = type_param.default_type().map(TypeRef::from_ast); | ||
108 | let param = GenericParam { idx: idx as u32 + start, name: name.clone(), default }; | ||
109 | self.params.push(param); | ||
110 | |||
111 | let type_ref = TypeRef::Path(name.into()); | ||
112 | self.fill_bounds(&type_param, type_ref); | ||
113 | } | ||
114 | } | ||
115 | |||
116 | fn fill_where_predicates(&mut self, where_clause: ast::WhereClause) { | ||
117 | for pred in where_clause.predicates() { | ||
118 | let type_ref = match pred.type_ref() { | ||
119 | Some(type_ref) => type_ref, | ||
120 | None => continue, | ||
121 | }; | ||
122 | let type_ref = TypeRef::from_ast(type_ref); | ||
123 | for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) { | ||
124 | self.add_where_predicate_from_bound(bound, type_ref.clone()); | ||
125 | } | ||
126 | } | ||
127 | } | ||
128 | |||
129 | fn add_where_predicate_from_bound(&mut self, bound: ast::TypeBound, type_ref: TypeRef) { | ||
130 | if bound.has_question_mark() { | ||
131 | // FIXME: remove this bound | ||
132 | return; | ||
133 | } | ||
134 | let bound = TypeBound::from_ast(bound); | ||
135 | self.where_predicates.push(WherePredicate { type_ref, bound }); | ||
136 | } | ||
137 | |||
138 | pub fn find_by_name(&self, name: &Name) -> Option<&GenericParam> { | ||
139 | self.params.iter().find(|p| &p.name == name) | ||
140 | } | ||
141 | |||
142 | pub fn count_parent_params(&self) -> usize { | ||
143 | self.parent_params.as_ref().map(|p| p.count_params_including_parent()).unwrap_or(0) | ||
144 | } | ||
145 | |||
146 | pub fn count_params_including_parent(&self) -> usize { | ||
147 | let parent_count = self.count_parent_params(); | ||
148 | parent_count + self.params.len() | ||
149 | } | ||
150 | |||
151 | fn for_each_param<'a>(&'a self, f: &mut impl FnMut(&'a GenericParam)) { | ||
152 | if let Some(parent) = &self.parent_params { | ||
153 | parent.for_each_param(f); | ||
154 | } | ||
155 | self.params.iter().for_each(f); | ||
156 | } | ||
157 | |||
158 | pub fn params_including_parent(&self) -> Vec<&GenericParam> { | ||
159 | let mut vec = Vec::with_capacity(self.count_params_including_parent()); | ||
160 | self.for_each_param(&mut |p| vec.push(p)); | ||
161 | vec | ||
162 | } | ||
163 | } | ||
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index 50caf4f83..dffc82ff8 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs | |||
@@ -17,6 +17,7 @@ pub mod imp; | |||
17 | pub mod diagnostics; | 17 | pub mod diagnostics; |
18 | pub mod expr; | 18 | pub mod expr; |
19 | pub mod body; | 19 | pub mod body; |
20 | pub mod generics; | ||
20 | 21 | ||
21 | #[cfg(test)] | 22 | #[cfg(test)] |
22 | mod test_db; | 23 | mod test_db; |
@@ -408,3 +409,26 @@ pub enum AssocItemId { | |||
408 | // require not implementing From, and instead having some checked way of | 409 | // require not implementing From, and instead having some checked way of |
409 | // casting them, and somehow making the constructors private, which would be annoying. | 410 | // casting them, and somehow making the constructors private, which would be annoying. |
410 | impl_froms!(AssocItemId: FunctionId, ConstId, TypeAliasId); | 411 | impl_froms!(AssocItemId: FunctionId, ConstId, TypeAliasId); |
412 | |||
413 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] | ||
414 | pub enum GenericDefId { | ||
415 | FunctionId(FunctionId), | ||
416 | AdtId(AdtId), | ||
417 | TraitId(TraitId), | ||
418 | TypeAliasId(TypeAliasId), | ||
419 | ImplId(ImplId), | ||
420 | // enum variants cannot have generics themselves, but their parent enums | ||
421 | // can, and this makes some code easier to write | ||
422 | EnumVariantId(EnumVariantId), | ||
423 | // consts can have type parameters from their parents (i.e. associated consts of traits) | ||
424 | ConstId(ConstId), | ||
425 | } | ||
426 | impl_froms!( | ||
427 | GenericDefId: FunctionId, | ||
428 | AdtId(StructId, EnumId, UnionId), | ||
429 | TraitId, | ||
430 | TypeAliasId, | ||
431 | ImplId, | ||
432 | EnumVariantId, | ||
433 | ConstId | ||
434 | ); | ||