1 //! Defines hir-level representation of structs, enums and unions
3 use std::{num::NonZeroU32, sync::Arc};
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},
17 builtin_type::{BuiltinInt, BuiltinUint},
20 item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId},
21 nameres::diagnostics::DefDiagnostic,
26 visibility::RawVisibility,
27 EnumId, LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StructId, UnionId,
32 /// Note that we use `StructData` for unions as well!
33 #[derive(Debug, Clone, PartialEq, Eq)]
34 pub struct StructData {
36 pub variant_data: Arc<VariantData>,
37 pub repr: Option<ReprData>,
38 pub visibility: RawVisibility,
41 #[derive(Debug, Clone, PartialEq, Eq)]
44 pub variants: Arena<EnumVariantData>,
45 pub repr: Option<ReprData>,
46 pub visibility: RawVisibility,
49 #[derive(Debug, Clone, PartialEq, Eq)]
50 pub struct EnumVariantData {
52 pub variant_data: Arc<VariantData>,
55 #[derive(Debug, Clone, PartialEq, Eq)]
56 pub enum VariantData {
57 Record(Arena<FieldData>),
58 Tuple(Arena<FieldData>),
62 /// A single field of an enum variant or struct
63 #[derive(Debug, Clone, PartialEq, Eq)]
64 pub struct FieldData {
66 pub type_ref: Interned<TypeRef>,
67 pub visibility: RawVisibility,
70 #[derive(Copy, Debug, Clone, PartialEq, Eq)]
73 BuiltinInt { builtin: Either<BuiltinInt, BuiltinUint>, is_c: bool },
78 #[derive(Copy, Debug, Clone, PartialEq, Eq)]
82 pub align: Option<NonZeroU32>,
90 ) -> Option<ReprData> {
91 item_tree.attrs(db, krate, of).by_key("repr").tt_values().find_map(parse_repr_tt)
94 fn parse_repr_tt(tt: &Subtree) -> Option<ReprData> {
96 Some(Delimiter { kind: DelimiterKind::Parenthesis, .. }) => {}
100 let mut data = ReprData { kind: ReprKind::Default, packed: false, align: None };
102 let mut tts = tt.token_trees.iter().peekable();
103 while let Some(tt) = tts.next() {
104 if let TokenTree::Leaf(Leaf::Ident(ident)) = tt {
108 if let Some(TokenTree::Subtree(_)) = tts.peek() {
113 if let Some(TokenTree::Subtree(tt)) = tts.peek() {
115 if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() {
116 if let Ok(align) = lit.text.parse() {
117 data.align = Some(align);
123 if let ReprKind::BuiltinInt { is_c, .. } = &mut data.kind {
126 data.kind = ReprKind::C;
129 "transparent" => data.kind = ReprKind::Transparent,
131 let is_c = matches!(data.kind, ReprKind::C);
132 if let Some(builtin) = BuiltinInt::from_suffix(repr)
134 .or_else(|| BuiltinUint::from_suffix(repr).map(Either::Right))
136 data.kind = ReprKind::BuiltinInt { builtin, is_c };
147 pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> {
148 db.struct_data_with_diagnostics(id).0
151 pub(crate) fn struct_data_with_diagnostics_query(
152 db: &dyn DefDatabase,
154 ) -> (Arc<StructData>, Arc<[DefDiagnostic]>) {
155 let loc = id.lookup(db);
156 let krate = loc.container.krate;
157 let item_tree = loc.id.item_tree(db);
158 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
159 let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
161 let strukt = &item_tree[loc.id.value];
162 let (variant_data, diagnostics) = lower_fields(
166 loc.container.local_id,
173 Arc::new(StructData {
174 name: strukt.name.clone(),
175 variant_data: Arc::new(variant_data),
177 visibility: item_tree[strukt.visibility].clone(),
183 pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> {
184 db.union_data_with_diagnostics(id).0
187 pub(crate) fn union_data_with_diagnostics_query(
188 db: &dyn DefDatabase,
190 ) -> (Arc<StructData>, Arc<[DefDiagnostic]>) {
191 let loc = id.lookup(db);
192 let krate = loc.container.krate;
193 let item_tree = loc.id.item_tree(db);
194 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
195 let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
197 let union = &item_tree[loc.id.value];
198 let (variant_data, diagnostics) = lower_fields(
202 loc.container.local_id,
209 Arc::new(StructData {
210 name: union.name.clone(),
211 variant_data: Arc::new(variant_data),
213 visibility: item_tree[union.visibility].clone(),
221 pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> {
222 db.enum_data_with_diagnostics(e).0
225 pub(crate) fn enum_data_with_diagnostics_query(
226 db: &dyn DefDatabase,
228 ) -> (Arc<EnumData>, Arc<[DefDiagnostic]>) {
229 let loc = e.lookup(db);
230 let krate = loc.container.krate;
231 let item_tree = loc.id.item_tree(db);
232 let cfg_options = db.crate_graph()[krate].cfg_options.clone();
233 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
235 let enum_ = &item_tree[loc.id.value];
236 let mut variants = Arena::new();
237 let mut diagnostics = Vec::new();
238 for tree_id in enum_.variants.clone() {
239 let attrs = item_tree.attrs(db, krate, tree_id.into());
240 let var = &item_tree[tree_id];
241 if attrs.is_cfg_enabled(&cfg_options) {
242 let (var_data, field_diagnostics) = lower_fields(
246 loc.container.local_id,
250 Some(enum_.visibility),
252 diagnostics.extend(field_diagnostics);
254 variants.alloc(EnumVariantData {
255 name: var.name.clone(),
256 variant_data: Arc::new(var_data),
259 diagnostics.push(DefDiagnostic::unconfigured_code(
260 loc.container.local_id,
261 InFile::new(loc.id.file_id(), var.ast_id.upcast()),
262 attrs.cfg().unwrap(),
270 name: enum_.name.clone(),
273 visibility: item_tree[enum_.visibility].clone(),
279 pub fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> {
280 let (id, _) = self.variants.iter().find(|(_id, data)| &data.name == name)?;
284 pub fn variant_body_type(&self) -> Either<BuiltinInt, BuiltinUint> {
286 Some(ReprData { kind: ReprKind::BuiltinInt { builtin, .. }, .. }) => builtin,
287 _ => Either::Left(BuiltinInt::Isize),
292 impl HasChildSource<LocalEnumVariantId> for EnumId {
293 type Value = ast::Variant;
296 db: &dyn DefDatabase,
297 ) -> InFile<ArenaMap<LocalEnumVariantId, Self::Value>> {
298 let src = self.lookup(db).source(db);
299 let mut trace = Trace::new_for_map();
300 lower_enum(db, &mut trace, &src, self.lookup(db).container);
301 src.with_value(trace.into_map())
306 db: &dyn DefDatabase,
307 trace: &mut Trace<EnumVariantData, ast::Variant>,
308 ast: &InFile<ast::Enum>,
311 let expander = CfgExpander::new(db, ast.file_id, module_id.krate);
316 .flat_map(|it| it.variants())
317 .filter(|var| expander.is_cfg_enabled(db, var));
318 for var in variants {
322 name: var.name().map_or_else(Name::missing, |it| it.as_name()),
323 variant_data: Arc::new(VariantData::new(db, ast.with_value(var.kind()), module_id)),
330 fn new(db: &dyn DefDatabase, flavor: InFile<ast::StructKind>, module_id: ModuleId) -> Self {
331 let mut expander = CfgExpander::new(db, flavor.file_id, module_id.krate);
332 let mut trace = Trace::new_for_arena();
333 match lower_struct(db, &mut expander, &mut trace, &flavor) {
334 StructKind::Tuple => VariantData::Tuple(trace.into_arena()),
335 StructKind::Record => VariantData::Record(trace.into_arena()),
336 StructKind::Unit => VariantData::Unit,
340 pub fn fields(&self) -> &Arena<FieldData> {
341 const EMPTY: &Arena<FieldData> = &Arena::new();
343 VariantData::Record(fields) | VariantData::Tuple(fields) => fields,
348 pub fn field(&self, name: &Name) -> Option<LocalFieldId> {
349 self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None })
352 pub fn kind(&self) -> StructKind {
354 VariantData::Record(_) => StructKind::Record,
355 VariantData::Tuple(_) => StructKind::Tuple,
356 VariantData::Unit => StructKind::Unit,
361 impl HasChildSource<LocalFieldId> for VariantId {
362 type Value = Either<ast::TupleField, ast::RecordField>;
364 fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<LocalFieldId, Self::Value>> {
365 let (src, module_id) = match self {
366 VariantId::EnumVariantId(it) => {
367 // I don't really like the fact that we call into parent source
368 // here, this might add to more queries then necessary.
369 let src = it.parent.child_source(db);
370 (src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container)
372 VariantId::StructId(it) => {
373 (it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container)
375 VariantId::UnionId(it) => (
376 it.lookup(db).source(db).map(|it| {
377 it.record_field_list()
378 .map(ast::StructKind::Record)
379 .unwrap_or(ast::StructKind::Unit)
381 it.lookup(db).container,
384 let mut expander = CfgExpander::new(db, src.file_id, module_id.krate);
385 let mut trace = Trace::new_for_map();
386 lower_struct(db, &mut expander, &mut trace, &src);
387 src.with_value(trace.into_map())
391 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
392 pub enum StructKind {
399 db: &dyn DefDatabase,
400 expander: &mut CfgExpander,
401 trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
402 ast: &InFile<ast::StructKind>,
404 let ctx = LowerCtx::new(db, ast.file_id);
407 ast::StructKind::Tuple(fl) => {
408 for (i, fd) in fl.fields().enumerate() {
409 if !expander.is_cfg_enabled(db, &fd) {
414 || Either::Left(fd.clone()),
416 name: Name::new_tuple_field(i),
417 type_ref: Interned::new(TypeRef::from_ast_opt(&ctx, fd.ty())),
418 visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
424 ast::StructKind::Record(fl) => {
425 for fd in fl.fields() {
426 if !expander.is_cfg_enabled(db, &fd) {
431 || Either::Right(fd.clone()),
433 name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
434 type_ref: Interned::new(TypeRef::from_ast_opt(&ctx, fd.ty())),
435 visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
441 ast::StructKind::Unit => StructKind::Unit,
446 db: &dyn DefDatabase,
448 current_file_id: HirFileId,
449 container: LocalModuleId,
450 item_tree: &ItemTree,
451 cfg_options: &CfgOptions,
453 override_visibility: Option<RawVisibilityId>,
454 ) -> (VariantData, Vec<DefDiagnostic>) {
455 let mut diagnostics = Vec::new();
457 Fields::Record(flds) => {
458 let mut arena = Arena::new();
459 for field_id in flds.clone() {
460 let attrs = item_tree.attrs(db, krate, field_id.into());
461 let field = &item_tree[field_id];
462 if attrs.is_cfg_enabled(cfg_options) {
463 arena.alloc(lower_field(item_tree, field, override_visibility));
465 diagnostics.push(DefDiagnostic::unconfigured_code(
470 FieldAstId::Record(it) => it.upcast(),
471 FieldAstId::Tuple(it) => it.upcast(),
474 attrs.cfg().unwrap(),
479 (VariantData::Record(arena), diagnostics)
481 Fields::Tuple(flds) => {
482 let mut arena = Arena::new();
483 for field_id in flds.clone() {
484 let attrs = item_tree.attrs(db, krate, field_id.into());
485 let field = &item_tree[field_id];
486 if attrs.is_cfg_enabled(cfg_options) {
487 arena.alloc(lower_field(item_tree, field, override_visibility));
489 diagnostics.push(DefDiagnostic::unconfigured_code(
494 FieldAstId::Record(it) => it.upcast(),
495 FieldAstId::Tuple(it) => it.upcast(),
498 attrs.cfg().unwrap(),
503 (VariantData::Tuple(arena), diagnostics)
505 Fields::Unit => (VariantData::Unit, diagnostics),
510 item_tree: &ItemTree,
512 override_visibility: Option<RawVisibilityId>,
515 name: field.name.clone(),
516 type_ref: field.type_ref.clone(),
517 visibility: item_tree[override_visibility.unwrap_or(field.visibility)].clone(),