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