diff options
Diffstat (limited to 'crates/hir_def/src/adt.rs')
-rw-r--r-- | crates/hir_def/src/adt.rs | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/crates/hir_def/src/adt.rs b/crates/hir_def/src/adt.rs new file mode 100644 index 000000000..d69ff2fc7 --- /dev/null +++ b/crates/hir_def/src/adt.rs | |||
@@ -0,0 +1,329 @@ | |||
1 | //! Defines hir-level representation of structs, enums and unions | ||
2 | |||
3 | use std::sync::Arc; | ||
4 | |||
5 | use arena::{map::ArenaMap, Arena}; | ||
6 | use either::Either; | ||
7 | use hir_expand::{ | ||
8 | name::{AsName, Name}, | ||
9 | InFile, | ||
10 | }; | ||
11 | use syntax::ast::{self, NameOwner, VisibilityOwner}; | ||
12 | use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree}; | ||
13 | |||
14 | use crate::{ | ||
15 | body::{CfgExpander, LowerCtx}, | ||
16 | db::DefDatabase, | ||
17 | item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem}, | ||
18 | src::HasChildSource, | ||
19 | src::HasSource, | ||
20 | trace::Trace, | ||
21 | type_ref::TypeRef, | ||
22 | visibility::RawVisibility, | ||
23 | EnumId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId, | ||
24 | VariantId, | ||
25 | }; | ||
26 | use cfg::CfgOptions; | ||
27 | |||
28 | /// Note that we use `StructData` for unions as well! | ||
29 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
30 | pub struct StructData { | ||
31 | pub name: Name, | ||
32 | pub variant_data: Arc<VariantData>, | ||
33 | pub repr: Option<ReprKind>, | ||
34 | } | ||
35 | |||
36 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
37 | pub struct EnumData { | ||
38 | pub name: Name, | ||
39 | pub variants: Arena<EnumVariantData>, | ||
40 | } | ||
41 | |||
42 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
43 | pub struct EnumVariantData { | ||
44 | pub name: Name, | ||
45 | pub variant_data: Arc<VariantData>, | ||
46 | } | ||
47 | |||
48 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
49 | pub enum VariantData { | ||
50 | Record(Arena<FieldData>), | ||
51 | Tuple(Arena<FieldData>), | ||
52 | Unit, | ||
53 | } | ||
54 | |||
55 | /// A single field of an enum variant or struct | ||
56 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
57 | pub struct FieldData { | ||
58 | pub name: Name, | ||
59 | pub type_ref: TypeRef, | ||
60 | pub visibility: RawVisibility, | ||
61 | } | ||
62 | |||
63 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
64 | pub enum ReprKind { | ||
65 | Packed, | ||
66 | Other, | ||
67 | } | ||
68 | |||
69 | fn repr_from_value(item_tree: &ItemTree, of: AttrOwner) -> Option<ReprKind> { | ||
70 | item_tree.attrs(of).by_key("repr").tt_values().find_map(parse_repr_tt) | ||
71 | } | ||
72 | |||
73 | fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> { | ||
74 | match tt.delimiter { | ||
75 | Some(Delimiter { kind: DelimiterKind::Parenthesis, .. }) => {} | ||
76 | _ => return None, | ||
77 | } | ||
78 | |||
79 | let mut it = tt.token_trees.iter(); | ||
80 | match it.next()? { | ||
81 | TokenTree::Leaf(Leaf::Ident(ident)) if ident.text == "packed" => Some(ReprKind::Packed), | ||
82 | _ => Some(ReprKind::Other), | ||
83 | } | ||
84 | } | ||
85 | |||
86 | impl StructData { | ||
87 | pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> { | ||
88 | let loc = id.lookup(db); | ||
89 | let item_tree = db.item_tree(loc.id.file_id); | ||
90 | let repr = repr_from_value(&item_tree, ModItem::from(loc.id.value).into()); | ||
91 | let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); | ||
92 | |||
93 | let strukt = &item_tree[loc.id.value]; | ||
94 | let variant_data = lower_fields(&item_tree, &cfg_options, &strukt.fields); | ||
95 | Arc::new(StructData { | ||
96 | name: strukt.name.clone(), | ||
97 | variant_data: Arc::new(variant_data), | ||
98 | repr, | ||
99 | }) | ||
100 | } | ||
101 | pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> { | ||
102 | let loc = id.lookup(db); | ||
103 | let item_tree = db.item_tree(loc.id.file_id); | ||
104 | let repr = repr_from_value(&item_tree, ModItem::from(loc.id.value).into()); | ||
105 | let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); | ||
106 | |||
107 | let union = &item_tree[loc.id.value]; | ||
108 | let variant_data = lower_fields(&item_tree, &cfg_options, &union.fields); | ||
109 | |||
110 | Arc::new(StructData { | ||
111 | name: union.name.clone(), | ||
112 | variant_data: Arc::new(variant_data), | ||
113 | repr, | ||
114 | }) | ||
115 | } | ||
116 | } | ||
117 | |||
118 | impl EnumData { | ||
119 | pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> { | ||
120 | let loc = e.lookup(db); | ||
121 | let item_tree = db.item_tree(loc.id.file_id); | ||
122 | let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); | ||
123 | |||
124 | let enum_ = &item_tree[loc.id.value]; | ||
125 | let mut variants = Arena::new(); | ||
126 | for var_id in enum_.variants.clone() { | ||
127 | if item_tree.attrs(var_id.into()).is_cfg_enabled(&cfg_options) { | ||
128 | let var = &item_tree[var_id]; | ||
129 | let var_data = lower_fields(&item_tree, &cfg_options, &var.fields); | ||
130 | |||
131 | variants.alloc(EnumVariantData { | ||
132 | name: var.name.clone(), | ||
133 | variant_data: Arc::new(var_data), | ||
134 | }); | ||
135 | } | ||
136 | } | ||
137 | |||
138 | Arc::new(EnumData { name: enum_.name.clone(), variants }) | ||
139 | } | ||
140 | |||
141 | pub fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> { | ||
142 | let (id, _) = self.variants.iter().find(|(_id, data)| &data.name == name)?; | ||
143 | Some(id) | ||
144 | } | ||
145 | } | ||
146 | |||
147 | impl HasChildSource for EnumId { | ||
148 | type ChildId = LocalEnumVariantId; | ||
149 | type Value = ast::Variant; | ||
150 | fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> { | ||
151 | let src = self.lookup(db).source(db); | ||
152 | let mut trace = Trace::new_for_map(); | ||
153 | lower_enum(db, &mut trace, &src, self.lookup(db).container.module(db)); | ||
154 | src.with_value(trace.into_map()) | ||
155 | } | ||
156 | } | ||
157 | |||
158 | fn lower_enum( | ||
159 | db: &dyn DefDatabase, | ||
160 | trace: &mut Trace<EnumVariantData, ast::Variant>, | ||
161 | ast: &InFile<ast::Enum>, | ||
162 | module_id: ModuleId, | ||
163 | ) { | ||
164 | let expander = CfgExpander::new(db, ast.file_id, module_id.krate); | ||
165 | let variants = ast | ||
166 | .value | ||
167 | .variant_list() | ||
168 | .into_iter() | ||
169 | .flat_map(|it| it.variants()) | ||
170 | .filter(|var| expander.is_cfg_enabled(var)); | ||
171 | for var in variants { | ||
172 | trace.alloc( | ||
173 | || var.clone(), | ||
174 | || EnumVariantData { | ||
175 | name: var.name().map_or_else(Name::missing, |it| it.as_name()), | ||
176 | variant_data: Arc::new(VariantData::new(db, ast.with_value(var.kind()), module_id)), | ||
177 | }, | ||
178 | ); | ||
179 | } | ||
180 | } | ||
181 | |||
182 | impl VariantData { | ||
183 | fn new(db: &dyn DefDatabase, flavor: InFile<ast::StructKind>, module_id: ModuleId) -> Self { | ||
184 | let mut expander = CfgExpander::new(db, flavor.file_id, module_id.krate); | ||
185 | let mut trace = Trace::new_for_arena(); | ||
186 | match lower_struct(db, &mut expander, &mut trace, &flavor) { | ||
187 | StructKind::Tuple => VariantData::Tuple(trace.into_arena()), | ||
188 | StructKind::Record => VariantData::Record(trace.into_arena()), | ||
189 | StructKind::Unit => VariantData::Unit, | ||
190 | } | ||
191 | } | ||
192 | |||
193 | pub fn fields(&self) -> &Arena<FieldData> { | ||
194 | const EMPTY: &Arena<FieldData> = &Arena::new(); | ||
195 | match &self { | ||
196 | VariantData::Record(fields) | VariantData::Tuple(fields) => fields, | ||
197 | _ => EMPTY, | ||
198 | } | ||
199 | } | ||
200 | |||
201 | pub fn field(&self, name: &Name) -> Option<LocalFieldId> { | ||
202 | self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None }) | ||
203 | } | ||
204 | |||
205 | pub fn kind(&self) -> StructKind { | ||
206 | match self { | ||
207 | VariantData::Record(_) => StructKind::Record, | ||
208 | VariantData::Tuple(_) => StructKind::Tuple, | ||
209 | VariantData::Unit => StructKind::Unit, | ||
210 | } | ||
211 | } | ||
212 | } | ||
213 | |||
214 | impl HasChildSource for VariantId { | ||
215 | type ChildId = LocalFieldId; | ||
216 | type Value = Either<ast::TupleField, ast::RecordField>; | ||
217 | |||
218 | fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> { | ||
219 | let (src, module_id) = match self { | ||
220 | VariantId::EnumVariantId(it) => { | ||
221 | // I don't really like the fact that we call into parent source | ||
222 | // here, this might add to more queries then necessary. | ||
223 | let src = it.parent.child_source(db); | ||
224 | (src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container.module(db)) | ||
225 | } | ||
226 | VariantId::StructId(it) => { | ||
227 | (it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container.module(db)) | ||
228 | } | ||
229 | VariantId::UnionId(it) => ( | ||
230 | it.lookup(db).source(db).map(|it| { | ||
231 | it.record_field_list() | ||
232 | .map(ast::StructKind::Record) | ||
233 | .unwrap_or(ast::StructKind::Unit) | ||
234 | }), | ||
235 | it.lookup(db).container.module(db), | ||
236 | ), | ||
237 | }; | ||
238 | let mut expander = CfgExpander::new(db, src.file_id, module_id.krate); | ||
239 | let mut trace = Trace::new_for_map(); | ||
240 | lower_struct(db, &mut expander, &mut trace, &src); | ||
241 | src.with_value(trace.into_map()) | ||
242 | } | ||
243 | } | ||
244 | |||
245 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
246 | pub enum StructKind { | ||
247 | Tuple, | ||
248 | Record, | ||
249 | Unit, | ||
250 | } | ||
251 | |||
252 | fn lower_struct( | ||
253 | db: &dyn DefDatabase, | ||
254 | expander: &mut CfgExpander, | ||
255 | trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>, | ||
256 | ast: &InFile<ast::StructKind>, | ||
257 | ) -> StructKind { | ||
258 | let ctx = LowerCtx::new(db, ast.file_id); | ||
259 | |||
260 | match &ast.value { | ||
261 | ast::StructKind::Tuple(fl) => { | ||
262 | for (i, fd) in fl.fields().enumerate() { | ||
263 | if !expander.is_cfg_enabled(&fd) { | ||
264 | continue; | ||
265 | } | ||
266 | |||
267 | trace.alloc( | ||
268 | || Either::Left(fd.clone()), | ||
269 | || FieldData { | ||
270 | name: Name::new_tuple_field(i), | ||
271 | type_ref: TypeRef::from_ast_opt(&ctx, fd.ty()), | ||
272 | visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())), | ||
273 | }, | ||
274 | ); | ||
275 | } | ||
276 | StructKind::Tuple | ||
277 | } | ||
278 | ast::StructKind::Record(fl) => { | ||
279 | for fd in fl.fields() { | ||
280 | if !expander.is_cfg_enabled(&fd) { | ||
281 | continue; | ||
282 | } | ||
283 | |||
284 | trace.alloc( | ||
285 | || Either::Right(fd.clone()), | ||
286 | || FieldData { | ||
287 | name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing), | ||
288 | type_ref: TypeRef::from_ast_opt(&ctx, fd.ty()), | ||
289 | visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())), | ||
290 | }, | ||
291 | ); | ||
292 | } | ||
293 | StructKind::Record | ||
294 | } | ||
295 | ast::StructKind::Unit => StructKind::Unit, | ||
296 | } | ||
297 | } | ||
298 | |||
299 | fn lower_fields(item_tree: &ItemTree, cfg_options: &CfgOptions, fields: &Fields) -> VariantData { | ||
300 | match fields { | ||
301 | Fields::Record(flds) => { | ||
302 | let mut arena = Arena::new(); | ||
303 | for field_id in flds.clone() { | ||
304 | if item_tree.attrs(field_id.into()).is_cfg_enabled(cfg_options) { | ||
305 | arena.alloc(lower_field(item_tree, &item_tree[field_id])); | ||
306 | } | ||
307 | } | ||
308 | VariantData::Record(arena) | ||
309 | } | ||
310 | Fields::Tuple(flds) => { | ||
311 | let mut arena = Arena::new(); | ||
312 | for field_id in flds.clone() { | ||
313 | if item_tree.attrs(field_id.into()).is_cfg_enabled(cfg_options) { | ||
314 | arena.alloc(lower_field(item_tree, &item_tree[field_id])); | ||
315 | } | ||
316 | } | ||
317 | VariantData::Tuple(arena) | ||
318 | } | ||
319 | Fields::Unit => VariantData::Unit, | ||
320 | } | ||
321 | } | ||
322 | |||
323 | fn lower_field(item_tree: &ItemTree, field: &Field) -> FieldData { | ||
324 | FieldData { | ||
325 | name: field.name.clone(), | ||
326 | type_ref: field.type_ref.clone(), | ||
327 | visibility: item_tree[field.visibility].clone(), | ||
328 | } | ||
329 | } | ||