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