1 //! Defines hir-level representation of structs, enums and unions
11 use la_arena::{Arena, ArenaMap};
12 use syntax::ast::{self, HasName, HasVisibility};
13 use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
16 body::{CfgExpander, LowerCtx},
19 item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem, RawVisibilityId},
24 visibility::RawVisibility,
25 EnumId, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId, VariantId,
29 /// Note that we use `StructData` for unions as well!
30 #[derive(Debug, Clone, PartialEq, Eq)]
31 pub struct StructData {
33 pub variant_data: Arc<VariantData>,
34 pub repr: Option<ReprKind>,
35 pub visibility: RawVisibility,
38 #[derive(Debug, Clone, PartialEq, Eq)]
41 pub variants: Arena<EnumVariantData>,
42 pub visibility: RawVisibility,
45 #[derive(Debug, Clone, PartialEq, Eq)]
46 pub struct EnumVariantData {
48 pub variant_data: Arc<VariantData>,
51 #[derive(Debug, Clone, PartialEq, Eq)]
52 pub enum VariantData {
53 Record(Arena<FieldData>),
54 Tuple(Arena<FieldData>),
58 /// A single field of an enum variant or struct
59 #[derive(Debug, Clone, PartialEq, Eq)]
60 pub struct FieldData {
62 pub type_ref: Interned<TypeRef>,
63 pub visibility: RawVisibility,
66 #[derive(Debug, Clone, PartialEq, Eq)]
77 ) -> Option<ReprKind> {
78 item_tree.attrs(db, krate, of).by_key("repr").tt_values().find_map(parse_repr_tt)
81 fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> {
83 Some(Delimiter { kind: DelimiterKind::Parenthesis, .. }) => {}
87 let mut it = tt.token_trees.iter();
89 TokenTree::Leaf(Leaf::Ident(ident)) if ident.text == "packed" => Some(ReprKind::Packed),
90 _ => Some(ReprKind::Other),
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();
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),
108 visibility: item_tree[strukt.visibility].clone(),
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();
118 let union = &item_tree[loc.id.value];
119 let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &union.fields, None);
121 Arc::new(StructData {
122 name: union.name.clone(),
123 variant_data: Arc::new(variant_data),
125 visibility: item_tree[union.visibility].clone(),
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();
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(
148 Some(enum_.visibility),
151 variants.alloc(EnumVariantData {
152 name: var.name.clone(),
153 variant_data: Arc::new(var_data),
159 name: enum_.name.clone(),
161 visibility: item_tree[enum_.visibility].clone(),
165 pub fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> {
166 let (id, _) = self.variants.iter().find(|(_id, data)| &data.name == name)?;
171 impl HasChildSource<LocalEnumVariantId> for EnumId {
172 type Value = ast::Variant;
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())
185 db: &dyn DefDatabase,
186 trace: &mut Trace<EnumVariantData, ast::Variant>,
187 ast: &InFile<ast::Enum>,
190 let expander = CfgExpander::new(db, ast.file_id, module_id.krate);
195 .flat_map(|it| it.variants())
196 .filter(|var| expander.is_cfg_enabled(db, var));
197 for var in variants {
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)),
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,
219 pub fn fields(&self) -> &Arena<FieldData> {
220 const EMPTY: &Arena<FieldData> = &Arena::new();
222 VariantData::Record(fields) | VariantData::Tuple(fields) => fields,
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 })
231 pub fn kind(&self) -> StructKind {
233 VariantData::Record(_) => StructKind::Record,
234 VariantData::Tuple(_) => StructKind::Tuple,
235 VariantData::Unit => StructKind::Unit,
240 impl HasChildSource<LocalFieldId> for VariantId {
241 type Value = Either<ast::TupleField, ast::RecordField>;
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)
251 VariantId::StructId(it) => {
252 (it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container)
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)
260 it.lookup(db).container,
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())
270 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
271 pub enum StructKind {
278 db: &dyn DefDatabase,
279 expander: &mut CfgExpander,
280 trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
281 ast: &InFile<ast::StructKind>,
283 let ctx = LowerCtx::new(db, ast.file_id);
286 ast::StructKind::Tuple(fl) => {
287 for (i, fd) in fl.fields().enumerate() {
288 if !expander.is_cfg_enabled(db, &fd) {
293 || Either::Left(fd.clone()),
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())),
303 ast::StructKind::Record(fl) => {
304 for fd in fl.fields() {
305 if !expander.is_cfg_enabled(db, &fd) {
310 || Either::Right(fd.clone()),
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())),
320 ast::StructKind::Unit => StructKind::Unit,
325 db: &dyn DefDatabase,
327 item_tree: &ItemTree,
328 cfg_options: &CfgOptions,
330 override_visibility: Option<RawVisibilityId>,
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));
340 VariantData::Record(arena)
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));
349 VariantData::Tuple(arena)
351 Fields::Unit => VariantData::Unit,
356 item_tree: &ItemTree,
358 override_visibility: Option<RawVisibilityId>,
361 name: field.name.clone(),
362 type_ref: field.type_ref.clone(),
363 visibility: item_tree[override_visibility.unwrap_or(field.visibility)].clone(),