aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src/adt.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def/src/adt.rs')
-rw-r--r--crates/hir_def/src/adt.rs329
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
3use std::sync::Arc;
4
5use arena::{map::ArenaMap, Arena};
6use either::Either;
7use hir_expand::{
8 name::{AsName, Name},
9 InFile,
10};
11use syntax::ast::{self, NameOwner, VisibilityOwner};
12use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
13
14use 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};
26use cfg::CfgOptions;
27
28/// Note that we use `StructData` for unions as well!
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub 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)]
37pub struct EnumData {
38 pub name: Name,
39 pub variants: Arena<EnumVariantData>,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub struct EnumVariantData {
44 pub name: Name,
45 pub variant_data: Arc<VariantData>,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub 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)]
57pub struct FieldData {
58 pub name: Name,
59 pub type_ref: TypeRef,
60 pub visibility: RawVisibility,
61}
62
63#[derive(Debug, Clone, PartialEq, Eq)]
64pub enum ReprKind {
65 Packed,
66 Other,
67}
68
69fn 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
73fn 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
86impl 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
118impl 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
147impl 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
158fn 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
182impl 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
214impl 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)]
246pub enum StructKind {
247 Tuple,
248 Record,
249 Unit,
250}
251
252fn 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
299fn 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
323fn 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}