]> git.lizzy.rs Git - rust.git/blob - crates/hir-def/src/adt.rs
785095800604b204d745b66411fc1c9173b66446
[rust.git] / crates / hir-def / src / adt.rs
1 //! Defines hir-level representation of structs, enums and unions
2
3 use std::{num::NonZeroU32, sync::Arc};
4
5 use base_db::CrateId;
6 use either::Either;
7 use hir_expand::{
8     name::{AsName, Name},
9     InFile,
10 };
11 use la_arena::{Arena, ArenaMap};
12 use syntax::ast::{self, HasName, HasVisibility};
13 use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
14
15 use crate::{
16     body::{CfgExpander, LowerCtx},
17     builtin_type::{BuiltinInt, BuiltinUint},
18     db::DefDatabase,
19     intern::Interned,
20     item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem, RawVisibilityId},
21     src::HasChildSource,
22     src::HasSource,
23     trace::Trace,
24     type_ref::TypeRef,
25     visibility::RawVisibility,
26     EnumId, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId, VariantId,
27 };
28 use cfg::CfgOptions;
29
30 /// Note that we use `StructData` for unions as well!
31 #[derive(Debug, Clone, PartialEq, Eq)]
32 pub struct StructData {
33     pub name: Name,
34     pub variant_data: Arc<VariantData>,
35     pub repr: Option<ReprData>,
36     pub visibility: RawVisibility,
37 }
38
39 #[derive(Debug, Clone, PartialEq, Eq)]
40 pub struct EnumData {
41     pub name: Name,
42     pub variants: Arena<EnumVariantData>,
43     pub repr: Option<ReprData>,
44     pub visibility: RawVisibility,
45 }
46
47 #[derive(Debug, Clone, PartialEq, Eq)]
48 pub struct EnumVariantData {
49     pub name: Name,
50     pub variant_data: Arc<VariantData>,
51 }
52
53 #[derive(Debug, Clone, PartialEq, Eq)]
54 pub enum VariantData {
55     Record(Arena<FieldData>),
56     Tuple(Arena<FieldData>),
57     Unit,
58 }
59
60 /// A single field of an enum variant or struct
61 #[derive(Debug, Clone, PartialEq, Eq)]
62 pub struct FieldData {
63     pub name: Name,
64     pub type_ref: Interned<TypeRef>,
65     pub visibility: RawVisibility,
66 }
67
68 #[derive(Copy, Debug, Clone, PartialEq, Eq)]
69 pub enum ReprKind {
70     C,
71     BuiltinInt { builtin: Either<BuiltinInt, BuiltinUint>, is_c: bool },
72     Transparent,
73     Default,
74 }
75
76 #[derive(Copy, Debug, Clone, PartialEq, Eq)]
77 pub struct ReprData {
78     pub kind: ReprKind,
79     pub packed: bool,
80     pub align: Option<NonZeroU32>,
81 }
82
83 fn repr_from_value(
84     db: &dyn DefDatabase,
85     krate: CrateId,
86     item_tree: &ItemTree,
87     of: AttrOwner,
88 ) -> Option<ReprData> {
89     item_tree.attrs(db, krate, of).by_key("repr").tt_values().find_map(parse_repr_tt)
90 }
91
92 fn parse_repr_tt(tt: &Subtree) -> Option<ReprData> {
93     match tt.delimiter {
94         Some(Delimiter { kind: DelimiterKind::Parenthesis, .. }) => {}
95         _ => return None,
96     }
97
98     let mut data = ReprData { kind: ReprKind::Default, packed: false, align: None };
99
100     let mut tts = tt.token_trees.iter().peekable();
101     while let Some(tt) = tts.next() {
102         if let TokenTree::Leaf(Leaf::Ident(ident)) = tt {
103             match &*ident.text {
104                 "packed" => {
105                     data.packed = true;
106                     if let Some(TokenTree::Subtree(_)) = tts.peek() {
107                         tts.next();
108                     }
109                 }
110                 "align" => {
111                     if let Some(TokenTree::Subtree(tt)) = tts.peek() {
112                         tts.next();
113                         if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() {
114                             if let Ok(align) = lit.text.parse() {
115                                 data.align = Some(align);
116                             }
117                         }
118                     }
119                 }
120                 "C" => {
121                     if let ReprKind::BuiltinInt { is_c, .. } = &mut data.kind {
122                         *is_c = true;
123                     } else {
124                         data.kind = ReprKind::C;
125                     }
126                 }
127                 "transparent" => data.kind = ReprKind::Transparent,
128                 repr => {
129                     let is_c = matches!(data.kind, ReprKind::C);
130                     if let Some(builtin) = BuiltinInt::from_suffix(repr)
131                         .map(Either::Left)
132                         .or_else(|| BuiltinUint::from_suffix(repr).map(Either::Right))
133                     {
134                         data.kind = ReprKind::BuiltinInt { builtin, is_c };
135                     }
136                 }
137             }
138         }
139     }
140
141     Some(data)
142 }
143
144 impl StructData {
145     pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> {
146         let loc = id.lookup(db);
147         let krate = loc.container.krate;
148         let item_tree = loc.id.item_tree(db);
149         let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
150         let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
151
152         let strukt = &item_tree[loc.id.value];
153         let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &strukt.fields, None);
154         Arc::new(StructData {
155             name: strukt.name.clone(),
156             variant_data: Arc::new(variant_data),
157             repr,
158             visibility: item_tree[strukt.visibility].clone(),
159         })
160     }
161
162     pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> {
163         let loc = id.lookup(db);
164         let krate = loc.container.krate;
165         let item_tree = loc.id.item_tree(db);
166         let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
167         let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
168
169         let union = &item_tree[loc.id.value];
170         let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &union.fields, None);
171
172         Arc::new(StructData {
173             name: union.name.clone(),
174             variant_data: Arc::new(variant_data),
175             repr,
176             visibility: item_tree[union.visibility].clone(),
177         })
178     }
179 }
180
181 impl EnumData {
182     pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> {
183         let loc = e.lookup(db);
184         let krate = loc.container.krate;
185         let item_tree = loc.id.item_tree(db);
186         let cfg_options = db.crate_graph()[krate].cfg_options.clone();
187         let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
188
189         let enum_ = &item_tree[loc.id.value];
190         let mut variants = Arena::new();
191         for tree_id in enum_.variants.clone() {
192             if item_tree.attrs(db, krate, tree_id.into()).is_cfg_enabled(&cfg_options) {
193                 let var = &item_tree[tree_id];
194                 let var_data = lower_fields(
195                     db,
196                     krate,
197                     &item_tree,
198                     &cfg_options,
199                     &var.fields,
200                     Some(enum_.visibility),
201                 );
202
203                 variants.alloc(EnumVariantData {
204                     name: var.name.clone(),
205                     variant_data: Arc::new(var_data),
206                 });
207             }
208         }
209
210         Arc::new(EnumData {
211             name: enum_.name.clone(),
212             variants,
213             repr,
214             visibility: item_tree[enum_.visibility].clone(),
215         })
216     }
217
218     pub fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> {
219         let (id, _) = self.variants.iter().find(|(_id, data)| &data.name == name)?;
220         Some(id)
221     }
222 }
223
224 impl HasChildSource<LocalEnumVariantId> for EnumId {
225     type Value = ast::Variant;
226     fn child_source(
227         &self,
228         db: &dyn DefDatabase,
229     ) -> InFile<ArenaMap<LocalEnumVariantId, Self::Value>> {
230         let src = self.lookup(db).source(db);
231         let mut trace = Trace::new_for_map();
232         lower_enum(db, &mut trace, &src, self.lookup(db).container);
233         src.with_value(trace.into_map())
234     }
235 }
236
237 fn lower_enum(
238     db: &dyn DefDatabase,
239     trace: &mut Trace<EnumVariantData, ast::Variant>,
240     ast: &InFile<ast::Enum>,
241     module_id: ModuleId,
242 ) {
243     let expander = CfgExpander::new(db, ast.file_id, module_id.krate);
244     let variants = ast
245         .value
246         .variant_list()
247         .into_iter()
248         .flat_map(|it| it.variants())
249         .filter(|var| expander.is_cfg_enabled(db, var));
250     for var in variants {
251         trace.alloc(
252             || var.clone(),
253             || EnumVariantData {
254                 name: var.name().map_or_else(Name::missing, |it| it.as_name()),
255                 variant_data: Arc::new(VariantData::new(db, ast.with_value(var.kind()), module_id)),
256             },
257         );
258     }
259 }
260
261 impl VariantData {
262     fn new(db: &dyn DefDatabase, flavor: InFile<ast::StructKind>, module_id: ModuleId) -> Self {
263         let mut expander = CfgExpander::new(db, flavor.file_id, module_id.krate);
264         let mut trace = Trace::new_for_arena();
265         match lower_struct(db, &mut expander, &mut trace, &flavor) {
266             StructKind::Tuple => VariantData::Tuple(trace.into_arena()),
267             StructKind::Record => VariantData::Record(trace.into_arena()),
268             StructKind::Unit => VariantData::Unit,
269         }
270     }
271
272     pub fn fields(&self) -> &Arena<FieldData> {
273         const EMPTY: &Arena<FieldData> = &Arena::new();
274         match &self {
275             VariantData::Record(fields) | VariantData::Tuple(fields) => fields,
276             _ => EMPTY,
277         }
278     }
279
280     pub fn field(&self, name: &Name) -> Option<LocalFieldId> {
281         self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None })
282     }
283
284     pub fn kind(&self) -> StructKind {
285         match self {
286             VariantData::Record(_) => StructKind::Record,
287             VariantData::Tuple(_) => StructKind::Tuple,
288             VariantData::Unit => StructKind::Unit,
289         }
290     }
291 }
292
293 impl HasChildSource<LocalFieldId> for VariantId {
294     type Value = Either<ast::TupleField, ast::RecordField>;
295
296     fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<LocalFieldId, Self::Value>> {
297         let (src, module_id) = match self {
298             VariantId::EnumVariantId(it) => {
299                 // I don't really like the fact that we call into parent source
300                 // here, this might add to more queries then necessary.
301                 let src = it.parent.child_source(db);
302                 (src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container)
303             }
304             VariantId::StructId(it) => {
305                 (it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container)
306             }
307             VariantId::UnionId(it) => (
308                 it.lookup(db).source(db).map(|it| {
309                     it.record_field_list()
310                         .map(ast::StructKind::Record)
311                         .unwrap_or(ast::StructKind::Unit)
312                 }),
313                 it.lookup(db).container,
314             ),
315         };
316         let mut expander = CfgExpander::new(db, src.file_id, module_id.krate);
317         let mut trace = Trace::new_for_map();
318         lower_struct(db, &mut expander, &mut trace, &src);
319         src.with_value(trace.into_map())
320     }
321 }
322
323 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
324 pub enum StructKind {
325     Tuple,
326     Record,
327     Unit,
328 }
329
330 fn lower_struct(
331     db: &dyn DefDatabase,
332     expander: &mut CfgExpander,
333     trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
334     ast: &InFile<ast::StructKind>,
335 ) -> StructKind {
336     let ctx = LowerCtx::new(db, ast.file_id);
337
338     match &ast.value {
339         ast::StructKind::Tuple(fl) => {
340             for (i, fd) in fl.fields().enumerate() {
341                 if !expander.is_cfg_enabled(db, &fd) {
342                     continue;
343                 }
344
345                 trace.alloc(
346                     || Either::Left(fd.clone()),
347                     || FieldData {
348                         name: Name::new_tuple_field(i),
349                         type_ref: Interned::new(TypeRef::from_ast_opt(&ctx, fd.ty())),
350                         visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
351                     },
352                 );
353             }
354             StructKind::Tuple
355         }
356         ast::StructKind::Record(fl) => {
357             for fd in fl.fields() {
358                 if !expander.is_cfg_enabled(db, &fd) {
359                     continue;
360                 }
361
362                 trace.alloc(
363                     || Either::Right(fd.clone()),
364                     || FieldData {
365                         name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
366                         type_ref: Interned::new(TypeRef::from_ast_opt(&ctx, fd.ty())),
367                         visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
368                     },
369                 );
370             }
371             StructKind::Record
372         }
373         ast::StructKind::Unit => StructKind::Unit,
374     }
375 }
376
377 fn lower_fields(
378     db: &dyn DefDatabase,
379     krate: CrateId,
380     item_tree: &ItemTree,
381     cfg_options: &CfgOptions,
382     fields: &Fields,
383     override_visibility: Option<RawVisibilityId>,
384 ) -> VariantData {
385     match fields {
386         Fields::Record(flds) => {
387             let mut arena = Arena::new();
388             for field_id in flds.clone() {
389                 if item_tree.attrs(db, krate, field_id.into()).is_cfg_enabled(cfg_options) {
390                     arena.alloc(lower_field(item_tree, &item_tree[field_id], override_visibility));
391                 }
392             }
393             VariantData::Record(arena)
394         }
395         Fields::Tuple(flds) => {
396             let mut arena = Arena::new();
397             for field_id in flds.clone() {
398                 if item_tree.attrs(db, krate, field_id.into()).is_cfg_enabled(cfg_options) {
399                     arena.alloc(lower_field(item_tree, &item_tree[field_id], override_visibility));
400                 }
401             }
402             VariantData::Tuple(arena)
403         }
404         Fields::Unit => VariantData::Unit,
405     }
406 }
407
408 fn lower_field(
409     item_tree: &ItemTree,
410     field: &Field,
411     override_visibility: Option<RawVisibilityId>,
412 ) -> FieldData {
413     FieldData {
414         name: field.name.clone(),
415         type_ref: field.type_ref.clone(),
416         visibility: item_tree[override_visibility.unwrap_or(field.visibility)].clone(),
417     }
418 }