• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Defines hir-level representation of structs, enums and unions
2 
3 use base_db::CrateId;
4 use bitflags::bitflags;
5 use cfg::CfgOptions;
6 use either::Either;
7 
8 use hir_expand::{
9     name::{AsName, Name},
10     HirFileId, InFile,
11 };
12 use intern::Interned;
13 use la_arena::{Arena, ArenaMap};
14 use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions};
15 use syntax::ast::{self, HasName, HasVisibility};
16 use triomphe::Arc;
17 
18 use crate::{
19     builtin_type::{BuiltinInt, BuiltinUint},
20     db::DefDatabase,
21     expander::CfgExpander,
22     item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId},
23     lang_item::LangItem,
24     lower::LowerCtx,
25     nameres::diagnostics::DefDiagnostic,
26     src::HasChildSource,
27     src::HasSource,
28     trace::Trace,
29     tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree},
30     type_ref::TypeRef,
31     visibility::RawVisibility,
32     EnumId, LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StructId, UnionId,
33     VariantId,
34 };
35 
36 /// Note that we use `StructData` for unions as well!
37 #[derive(Debug, Clone, PartialEq, Eq)]
38 pub struct StructData {
39     pub name: Name,
40     pub variant_data: Arc<VariantData>,
41     pub repr: Option<ReprOptions>,
42     pub visibility: RawVisibility,
43     pub flags: StructFlags,
44 }
45 
46 bitflags! {
47 #[derive(Debug, Clone, PartialEq, Eq)]
48     pub struct StructFlags: u8 {
49         const NO_FLAGS         = 0;
50         /// Indicates whether the struct is `PhantomData`.
51         const IS_PHANTOM_DATA  = 1 << 2;
52         /// Indicates whether the struct has a `#[fundamental]` attribute.
53         const IS_FUNDAMENTAL   = 1 << 3;
54         // FIXME: should this be a flag?
55         /// Indicates whether the struct has a `#[rustc_has_incoherent_inherent_impls]` attribute.
56         const IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL      = 1 << 4;
57         /// Indicates whether this struct is `Box`.
58         const IS_BOX           = 1 << 5;
59         /// Indicates whether this struct is `ManuallyDrop`.
60         const IS_MANUALLY_DROP = 1 << 6;
61         /// Indicates whether this struct is `UnsafeCell`.
62         const IS_UNSAFE_CELL   = 1 << 7;
63     }
64 }
65 
66 #[derive(Debug, Clone, PartialEq, Eq)]
67 pub struct EnumData {
68     pub name: Name,
69     pub variants: Arena<EnumVariantData>,
70     pub repr: Option<ReprOptions>,
71     pub visibility: RawVisibility,
72     pub rustc_has_incoherent_inherent_impls: bool,
73 }
74 
75 #[derive(Debug, Clone, PartialEq, Eq)]
76 pub struct EnumVariantData {
77     pub name: Name,
78     pub variant_data: Arc<VariantData>,
79 }
80 
81 #[derive(Debug, Clone, PartialEq, Eq)]
82 pub enum VariantData {
83     Record(Arena<FieldData>),
84     Tuple(Arena<FieldData>),
85     Unit,
86 }
87 
88 /// A single field of an enum variant or struct
89 #[derive(Debug, Clone, PartialEq, Eq)]
90 pub struct FieldData {
91     pub name: Name,
92     pub type_ref: Interned<TypeRef>,
93     pub visibility: RawVisibility,
94 }
95 
repr_from_value( db: &dyn DefDatabase, krate: CrateId, item_tree: &ItemTree, of: AttrOwner, ) -> Option<ReprOptions>96 fn repr_from_value(
97     db: &dyn DefDatabase,
98     krate: CrateId,
99     item_tree: &ItemTree,
100     of: AttrOwner,
101 ) -> Option<ReprOptions> {
102     item_tree.attrs(db, krate, of).by_key("repr").tt_values().find_map(parse_repr_tt)
103 }
104 
parse_repr_tt(tt: &Subtree) -> Option<ReprOptions>105 fn parse_repr_tt(tt: &Subtree) -> Option<ReprOptions> {
106     match tt.delimiter {
107         Delimiter { kind: DelimiterKind::Parenthesis, .. } => {}
108         _ => return None,
109     }
110 
111     let mut flags = ReprFlags::empty();
112     let mut int = None;
113     let mut max_align: Option<Align> = None;
114     let mut min_pack: Option<Align> = None;
115 
116     let mut tts = tt.token_trees.iter().peekable();
117     while let Some(tt) = tts.next() {
118         if let TokenTree::Leaf(Leaf::Ident(ident)) = tt {
119             flags.insert(match &*ident.text {
120                 "packed" => {
121                     let pack = if let Some(TokenTree::Subtree(tt)) = tts.peek() {
122                         tts.next();
123                         if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() {
124                             lit.text.parse().unwrap_or_default()
125                         } else {
126                             0
127                         }
128                     } else {
129                         0
130                     };
131                     let pack = Align::from_bytes(pack).unwrap();
132                     min_pack =
133                         Some(if let Some(min_pack) = min_pack { min_pack.min(pack) } else { pack });
134                     ReprFlags::empty()
135                 }
136                 "align" => {
137                     if let Some(TokenTree::Subtree(tt)) = tts.peek() {
138                         tts.next();
139                         if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() {
140                             if let Ok(align) = lit.text.parse() {
141                                 let align = Align::from_bytes(align).ok();
142                                 max_align = max_align.max(align);
143                             }
144                         }
145                     }
146                     ReprFlags::empty()
147                 }
148                 "C" => ReprFlags::IS_C,
149                 "transparent" => ReprFlags::IS_TRANSPARENT,
150                 repr => {
151                     if let Some(builtin) = BuiltinInt::from_suffix(repr)
152                         .map(Either::Left)
153                         .or_else(|| BuiltinUint::from_suffix(repr).map(Either::Right))
154                     {
155                         int = Some(match builtin {
156                             Either::Left(bi) => match bi {
157                                 BuiltinInt::Isize => IntegerType::Pointer(true),
158                                 BuiltinInt::I8 => IntegerType::Fixed(Integer::I8, true),
159                                 BuiltinInt::I16 => IntegerType::Fixed(Integer::I16, true),
160                                 BuiltinInt::I32 => IntegerType::Fixed(Integer::I32, true),
161                                 BuiltinInt::I64 => IntegerType::Fixed(Integer::I64, true),
162                                 BuiltinInt::I128 => IntegerType::Fixed(Integer::I128, true),
163                             },
164                             Either::Right(bu) => match bu {
165                                 BuiltinUint::Usize => IntegerType::Pointer(false),
166                                 BuiltinUint::U8 => IntegerType::Fixed(Integer::I8, false),
167                                 BuiltinUint::U16 => IntegerType::Fixed(Integer::I16, false),
168                                 BuiltinUint::U32 => IntegerType::Fixed(Integer::I32, false),
169                                 BuiltinUint::U64 => IntegerType::Fixed(Integer::I64, false),
170                                 BuiltinUint::U128 => IntegerType::Fixed(Integer::I128, false),
171                             },
172                         });
173                     }
174                     ReprFlags::empty()
175                 }
176             })
177         }
178     }
179 
180     Some(ReprOptions { int, align: max_align, pack: min_pack, flags, field_shuffle_seed: 0 })
181 }
182 
183 impl StructData {
struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData>184     pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> {
185         db.struct_data_with_diagnostics(id).0
186     }
187 
struct_data_with_diagnostics_query( db: &dyn DefDatabase, id: StructId, ) -> (Arc<StructData>, Arc<[DefDiagnostic]>)188     pub(crate) fn struct_data_with_diagnostics_query(
189         db: &dyn DefDatabase,
190         id: StructId,
191     ) -> (Arc<StructData>, Arc<[DefDiagnostic]>) {
192         let loc = id.lookup(db);
193         let krate = loc.container.krate;
194         let item_tree = loc.id.item_tree(db);
195         let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
196         let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
197 
198         let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
199 
200         let mut flags = StructFlags::NO_FLAGS;
201         if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
202             flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
203         }
204         if attrs.by_key("fundamental").exists() {
205             flags |= StructFlags::IS_FUNDAMENTAL;
206         }
207         if let Some(lang) = attrs.lang_item() {
208             match lang {
209                 LangItem::PhantomData => flags |= StructFlags::IS_PHANTOM_DATA,
210                 LangItem::OwnedBox => flags |= StructFlags::IS_BOX,
211                 LangItem::ManuallyDrop => flags |= StructFlags::IS_MANUALLY_DROP,
212                 LangItem::UnsafeCell => flags |= StructFlags::IS_UNSAFE_CELL,
213                 _ => (),
214             }
215         }
216 
217         let strukt = &item_tree[loc.id.value];
218         let (variant_data, diagnostics) = lower_fields(
219             db,
220             krate,
221             loc.id.file_id(),
222             loc.container.local_id,
223             &item_tree,
224             &cfg_options,
225             &strukt.fields,
226             None,
227         );
228         (
229             Arc::new(StructData {
230                 name: strukt.name.clone(),
231                 variant_data: Arc::new(variant_data),
232                 repr,
233                 visibility: item_tree[strukt.visibility].clone(),
234                 flags,
235             }),
236             diagnostics.into(),
237         )
238     }
239 
union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData>240     pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> {
241         db.union_data_with_diagnostics(id).0
242     }
243 
union_data_with_diagnostics_query( db: &dyn DefDatabase, id: UnionId, ) -> (Arc<StructData>, Arc<[DefDiagnostic]>)244     pub(crate) fn union_data_with_diagnostics_query(
245         db: &dyn DefDatabase,
246         id: UnionId,
247     ) -> (Arc<StructData>, Arc<[DefDiagnostic]>) {
248         let loc = id.lookup(db);
249         let krate = loc.container.krate;
250         let item_tree = loc.id.item_tree(db);
251         let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
252         let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
253 
254         let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
255         let mut flags = StructFlags::NO_FLAGS;
256         if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
257             flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
258         }
259         if attrs.by_key("fundamental").exists() {
260             flags |= StructFlags::IS_FUNDAMENTAL;
261         }
262 
263         let union = &item_tree[loc.id.value];
264         let (variant_data, diagnostics) = lower_fields(
265             db,
266             krate,
267             loc.id.file_id(),
268             loc.container.local_id,
269             &item_tree,
270             &cfg_options,
271             &union.fields,
272             None,
273         );
274         (
275             Arc::new(StructData {
276                 name: union.name.clone(),
277                 variant_data: Arc::new(variant_data),
278                 repr,
279                 visibility: item_tree[union.visibility].clone(),
280                 flags,
281             }),
282             diagnostics.into(),
283         )
284     }
285 }
286 
287 impl EnumData {
enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData>288     pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> {
289         db.enum_data_with_diagnostics(e).0
290     }
291 
enum_data_with_diagnostics_query( db: &dyn DefDatabase, e: EnumId, ) -> (Arc<EnumData>, Arc<[DefDiagnostic]>)292     pub(crate) fn enum_data_with_diagnostics_query(
293         db: &dyn DefDatabase,
294         e: EnumId,
295     ) -> (Arc<EnumData>, Arc<[DefDiagnostic]>) {
296         let loc = e.lookup(db);
297         let krate = loc.container.krate;
298         let item_tree = loc.id.item_tree(db);
299         let cfg_options = db.crate_graph()[krate].cfg_options.clone();
300         let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
301         let rustc_has_incoherent_inherent_impls = item_tree
302             .attrs(db, loc.container.krate, ModItem::from(loc.id.value).into())
303             .by_key("rustc_has_incoherent_inherent_impls")
304             .exists();
305 
306         let enum_ = &item_tree[loc.id.value];
307         let mut variants = Arena::new();
308         let mut diagnostics = Vec::new();
309         for tree_id in enum_.variants.clone() {
310             let attrs = item_tree.attrs(db, krate, tree_id.into());
311             let var = &item_tree[tree_id];
312             if attrs.is_cfg_enabled(&cfg_options) {
313                 let (var_data, field_diagnostics) = lower_fields(
314                     db,
315                     krate,
316                     loc.id.file_id(),
317                     loc.container.local_id,
318                     &item_tree,
319                     &cfg_options,
320                     &var.fields,
321                     Some(enum_.visibility),
322                 );
323                 diagnostics.extend(field_diagnostics);
324 
325                 variants.alloc(EnumVariantData {
326                     name: var.name.clone(),
327                     variant_data: Arc::new(var_data),
328                 });
329             } else {
330                 diagnostics.push(DefDiagnostic::unconfigured_code(
331                     loc.container.local_id,
332                     InFile::new(loc.id.file_id(), var.ast_id.upcast()),
333                     attrs.cfg().unwrap(),
334                     cfg_options.clone(),
335                 ))
336             }
337         }
338 
339         (
340             Arc::new(EnumData {
341                 name: enum_.name.clone(),
342                 variants,
343                 repr,
344                 visibility: item_tree[enum_.visibility].clone(),
345                 rustc_has_incoherent_inherent_impls,
346             }),
347             diagnostics.into(),
348         )
349     }
350 
variant(&self, name: &Name) -> Option<LocalEnumVariantId>351     pub fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> {
352         let (id, _) = self.variants.iter().find(|(_id, data)| &data.name == name)?;
353         Some(id)
354     }
355 
variant_body_type(&self) -> IntegerType356     pub fn variant_body_type(&self) -> IntegerType {
357         match self.repr {
358             Some(ReprOptions { int: Some(builtin), .. }) => builtin,
359             _ => IntegerType::Pointer(true),
360         }
361     }
362 }
363 
364 impl HasChildSource<LocalEnumVariantId> for EnumId {
365     type Value = ast::Variant;
child_source( &self, db: &dyn DefDatabase, ) -> InFile<ArenaMap<LocalEnumVariantId, Self::Value>>366     fn child_source(
367         &self,
368         db: &dyn DefDatabase,
369     ) -> InFile<ArenaMap<LocalEnumVariantId, Self::Value>> {
370         let src = self.lookup(db).source(db);
371         let mut trace = Trace::new_for_map();
372         lower_enum(db, &mut trace, &src, self.lookup(db).container);
373         src.with_value(trace.into_map())
374     }
375 }
376 
lower_enum( db: &dyn DefDatabase, trace: &mut Trace<EnumVariantData, ast::Variant>, ast: &InFile<ast::Enum>, module_id: ModuleId, )377 fn lower_enum(
378     db: &dyn DefDatabase,
379     trace: &mut Trace<EnumVariantData, ast::Variant>,
380     ast: &InFile<ast::Enum>,
381     module_id: ModuleId,
382 ) {
383     let expander = CfgExpander::new(db, ast.file_id, module_id.krate);
384     let variants = ast
385         .value
386         .variant_list()
387         .into_iter()
388         .flat_map(|it| it.variants())
389         .filter(|var| expander.is_cfg_enabled(db, var));
390     for var in variants {
391         trace.alloc(
392             || var.clone(),
393             || EnumVariantData {
394                 name: var.name().map_or_else(Name::missing, |it| it.as_name()),
395                 variant_data: Arc::new(VariantData::new(db, ast.with_value(var.kind()), module_id)),
396             },
397         );
398     }
399 }
400 
401 impl VariantData {
new(db: &dyn DefDatabase, flavor: InFile<ast::StructKind>, module_id: ModuleId) -> Self402     fn new(db: &dyn DefDatabase, flavor: InFile<ast::StructKind>, module_id: ModuleId) -> Self {
403         let mut expander = CfgExpander::new(db, flavor.file_id, module_id.krate);
404         let mut trace = Trace::new_for_arena();
405         match lower_struct(db, &mut expander, &mut trace, &flavor) {
406             StructKind::Tuple => VariantData::Tuple(trace.into_arena()),
407             StructKind::Record => VariantData::Record(trace.into_arena()),
408             StructKind::Unit => VariantData::Unit,
409         }
410     }
411 
fields(&self) -> &Arena<FieldData>412     pub fn fields(&self) -> &Arena<FieldData> {
413         const EMPTY: &Arena<FieldData> = &Arena::new();
414         match &self {
415             VariantData::Record(fields) | VariantData::Tuple(fields) => fields,
416             _ => EMPTY,
417         }
418     }
419 
field(&self, name: &Name) -> Option<LocalFieldId>420     pub fn field(&self, name: &Name) -> Option<LocalFieldId> {
421         self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None })
422     }
423 
kind(&self) -> StructKind424     pub fn kind(&self) -> StructKind {
425         match self {
426             VariantData::Record(_) => StructKind::Record,
427             VariantData::Tuple(_) => StructKind::Tuple,
428             VariantData::Unit => StructKind::Unit,
429         }
430     }
431 }
432 
433 impl HasChildSource<LocalFieldId> for VariantId {
434     type Value = Either<ast::TupleField, ast::RecordField>;
435 
child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<LocalFieldId, Self::Value>>436     fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<LocalFieldId, Self::Value>> {
437         let (src, module_id) = match self {
438             VariantId::EnumVariantId(it) => {
439                 // I don't really like the fact that we call into parent source
440                 // here, this might add to more queries then necessary.
441                 let src = it.parent.child_source(db);
442                 (src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container)
443             }
444             VariantId::StructId(it) => {
445                 (it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container)
446             }
447             VariantId::UnionId(it) => (
448                 it.lookup(db).source(db).map(|it| {
449                     it.record_field_list()
450                         .map(ast::StructKind::Record)
451                         .unwrap_or(ast::StructKind::Unit)
452                 }),
453                 it.lookup(db).container,
454             ),
455         };
456         let mut expander = CfgExpander::new(db, src.file_id, module_id.krate);
457         let mut trace = Trace::new_for_map();
458         lower_struct(db, &mut expander, &mut trace, &src);
459         src.with_value(trace.into_map())
460     }
461 }
462 
463 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
464 pub enum StructKind {
465     Tuple,
466     Record,
467     Unit,
468 }
469 
lower_struct( db: &dyn DefDatabase, expander: &mut CfgExpander, trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>, ast: &InFile<ast::StructKind>, ) -> StructKind470 fn lower_struct(
471     db: &dyn DefDatabase,
472     expander: &mut CfgExpander,
473     trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
474     ast: &InFile<ast::StructKind>,
475 ) -> StructKind {
476     let ctx = LowerCtx::new(db, &expander.hygiene(), ast.file_id);
477 
478     match &ast.value {
479         ast::StructKind::Tuple(fl) => {
480             for (i, fd) in fl.fields().enumerate() {
481                 if !expander.is_cfg_enabled(db, &fd) {
482                     continue;
483                 }
484 
485                 trace.alloc(
486                     || Either::Left(fd.clone()),
487                     || FieldData {
488                         name: Name::new_tuple_field(i),
489                         type_ref: Interned::new(TypeRef::from_ast_opt(&ctx, fd.ty())),
490                         visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
491                     },
492                 );
493             }
494             StructKind::Tuple
495         }
496         ast::StructKind::Record(fl) => {
497             for fd in fl.fields() {
498                 if !expander.is_cfg_enabled(db, &fd) {
499                     continue;
500                 }
501 
502                 trace.alloc(
503                     || Either::Right(fd.clone()),
504                     || FieldData {
505                         name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
506                         type_ref: Interned::new(TypeRef::from_ast_opt(&ctx, fd.ty())),
507                         visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
508                     },
509                 );
510             }
511             StructKind::Record
512         }
513         ast::StructKind::Unit => StructKind::Unit,
514     }
515 }
516 
lower_fields( db: &dyn DefDatabase, krate: CrateId, current_file_id: HirFileId, container: LocalModuleId, item_tree: &ItemTree, cfg_options: &CfgOptions, fields: &Fields, override_visibility: Option<RawVisibilityId>, ) -> (VariantData, Vec<DefDiagnostic>)517 fn lower_fields(
518     db: &dyn DefDatabase,
519     krate: CrateId,
520     current_file_id: HirFileId,
521     container: LocalModuleId,
522     item_tree: &ItemTree,
523     cfg_options: &CfgOptions,
524     fields: &Fields,
525     override_visibility: Option<RawVisibilityId>,
526 ) -> (VariantData, Vec<DefDiagnostic>) {
527     let mut diagnostics = Vec::new();
528     match fields {
529         Fields::Record(flds) => {
530             let mut arena = Arena::new();
531             for field_id in flds.clone() {
532                 let attrs = item_tree.attrs(db, krate, field_id.into());
533                 let field = &item_tree[field_id];
534                 if attrs.is_cfg_enabled(cfg_options) {
535                     arena.alloc(lower_field(item_tree, field, override_visibility));
536                 } else {
537                     diagnostics.push(DefDiagnostic::unconfigured_code(
538                         container,
539                         InFile::new(
540                             current_file_id,
541                             match field.ast_id {
542                                 FieldAstId::Record(it) => it.upcast(),
543                                 FieldAstId::Tuple(it) => it.upcast(),
544                             },
545                         ),
546                         attrs.cfg().unwrap(),
547                         cfg_options.clone(),
548                     ))
549                 }
550             }
551             (VariantData::Record(arena), diagnostics)
552         }
553         Fields::Tuple(flds) => {
554             let mut arena = Arena::new();
555             for field_id in flds.clone() {
556                 let attrs = item_tree.attrs(db, krate, field_id.into());
557                 let field = &item_tree[field_id];
558                 if attrs.is_cfg_enabled(cfg_options) {
559                     arena.alloc(lower_field(item_tree, field, override_visibility));
560                 } else {
561                     diagnostics.push(DefDiagnostic::unconfigured_code(
562                         container,
563                         InFile::new(
564                             current_file_id,
565                             match field.ast_id {
566                                 FieldAstId::Record(it) => it.upcast(),
567                                 FieldAstId::Tuple(it) => it.upcast(),
568                             },
569                         ),
570                         attrs.cfg().unwrap(),
571                         cfg_options.clone(),
572                     ))
573                 }
574             }
575             (VariantData::Tuple(arena), diagnostics)
576         }
577         Fields::Unit => (VariantData::Unit, diagnostics),
578     }
579 }
580 
lower_field( item_tree: &ItemTree, field: &Field, override_visibility: Option<RawVisibilityId>, ) -> FieldData581 fn lower_field(
582     item_tree: &ItemTree,
583     field: &Field,
584     override_visibility: Option<RawVisibilityId>,
585 ) -> FieldData {
586     FieldData {
587         name: field.name.clone(),
588         type_ref: field.type_ref.clone(),
589         visibility: item_tree[override_visibility.unwrap_or(field.visibility)].clone(),
590     }
591 }
592