1 //! Defines hir-level representation of structs, enums and unions
11 use la_arena::{Arena, ArenaMap};
12 use syntax::ast::{self, NameOwner, VisibilityOwner};
13 use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
16 body::{CfgExpander, LowerCtx},
18 item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem, RawVisibilityId},
23 visibility::RawVisibility,
24 EnumId, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId, VariantId,
28 /// Note that we use `StructData` for unions as well!
29 #[derive(Debug, Clone, PartialEq, Eq)]
30 pub struct StructData {
32 pub variant_data: Arc<VariantData>,
33 pub repr: Option<ReprKind>,
34 pub visibility: RawVisibility,
37 #[derive(Debug, Clone, PartialEq, Eq)]
40 pub variants: Arena<EnumVariantData>,
41 pub visibility: RawVisibility,
44 #[derive(Debug, Clone, PartialEq, Eq)]
45 pub struct EnumVariantData {
47 pub variant_data: Arc<VariantData>,
50 #[derive(Debug, Clone, PartialEq, Eq)]
51 pub enum VariantData {
52 Record(Arena<FieldData>),
53 Tuple(Arena<FieldData>),
57 /// A single field of an enum variant or struct
58 #[derive(Debug, Clone, PartialEq, Eq)]
59 pub struct FieldData {
61 pub type_ref: TypeRef,
62 pub visibility: RawVisibility,
65 #[derive(Debug, Clone, PartialEq, Eq)]
76 ) -> Option<ReprKind> {
77 item_tree.attrs(db, krate, of).by_key("repr").tt_values().find_map(parse_repr_tt)
80 fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> {
82 Some(Delimiter { kind: DelimiterKind::Parenthesis, .. }) => {}
86 let mut it = tt.token_trees.iter();
88 TokenTree::Leaf(Leaf::Ident(ident)) if ident.text == "packed" => Some(ReprKind::Packed),
89 _ => Some(ReprKind::Other),
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();
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),
107 visibility: item_tree[strukt.visibility].clone(),
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();
117 let union = &item_tree[loc.id.value];
118 let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &union.fields, None);
120 Arc::new(StructData {
121 name: union.name.clone(),
122 variant_data: Arc::new(variant_data),
124 visibility: item_tree[union.visibility].clone(),
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();
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(
147 Some(enum_.visibility),
150 variants.alloc(EnumVariantData {
151 name: var.name.clone(),
152 variant_data: Arc::new(var_data),
158 name: enum_.name.clone(),
160 visibility: item_tree[enum_.visibility].clone(),
164 pub fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> {
165 let (id, _) = self.variants.iter().find(|(_id, data)| &data.name == name)?;
170 impl HasChildSource<LocalEnumVariantId> for EnumId {
171 type Value = ast::Variant;
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())
184 db: &dyn DefDatabase,
185 trace: &mut Trace<EnumVariantData, ast::Variant>,
186 ast: &InFile<ast::Enum>,
189 let expander = CfgExpander::new(db, ast.file_id, module_id.krate);
194 .flat_map(|it| it.variants())
195 .filter(|var| expander.is_cfg_enabled(db, var));
196 for var in variants {
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)),
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,
218 pub fn fields(&self) -> &Arena<FieldData> {
219 const EMPTY: &Arena<FieldData> = &Arena::new();
221 VariantData::Record(fields) | VariantData::Tuple(fields) => fields,
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 })
230 pub fn kind(&self) -> StructKind {
232 VariantData::Record(_) => StructKind::Record,
233 VariantData::Tuple(_) => StructKind::Tuple,
234 VariantData::Unit => StructKind::Unit,
239 impl HasChildSource<LocalFieldId> for VariantId {
240 type Value = Either<ast::TupleField, ast::RecordField>;
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)
250 VariantId::StructId(it) => {
251 (it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container)
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)
259 it.lookup(db).container,
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())
269 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
270 pub enum StructKind {
277 db: &dyn DefDatabase,
278 expander: &mut CfgExpander,
279 trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
280 ast: &InFile<ast::StructKind>,
282 let ctx = LowerCtx::new(db, ast.file_id);
285 ast::StructKind::Tuple(fl) => {
286 for (i, fd) in fl.fields().enumerate() {
287 if !expander.is_cfg_enabled(db, &fd) {
292 || Either::Left(fd.clone()),
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())),
302 ast::StructKind::Record(fl) => {
303 for fd in fl.fields() {
304 if !expander.is_cfg_enabled(db, &fd) {
309 || Either::Right(fd.clone()),
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())),
319 ast::StructKind::Unit => StructKind::Unit,
324 db: &dyn DefDatabase,
326 item_tree: &ItemTree,
327 cfg_options: &CfgOptions,
329 override_visibility: Option<RawVisibilityId>,
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));
339 VariantData::Record(arena)
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));
348 VariantData::Tuple(arena)
350 Fields::Unit => VariantData::Unit,
355 item_tree: &ItemTree,
357 override_visibility: Option<RawVisibilityId>,
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(),