From 09f9733ca67b74057c55b6e96f38223a73db6a6e Mon Sep 17 00:00:00 2001
From: Aleksey Kladov <aleksey.kladov@gmail.com>
Date: Thu, 31 Oct 2019 16:40:36 +0300
Subject: [PATCH] move struct & enum data to hir_def

---
 crates/ra_hir/src/adt.rs            | 176 +---------------------------
 crates/ra_hir/src/code_model.rs     |  31 +++--
 crates/ra_hir/src/code_model/src.rs |  42 ++++++-
 crates/ra_hir/src/db.rs             |  15 +--
 crates/ra_hir/src/ty/lower.rs       |   8 +-
 crates/ra_hir_def/src/adt.rs        | 114 ++++++++++++++++++
 crates/ra_hir_def/src/db.rs         |  12 +-
 crates/ra_hir_def/src/lib.rs        |  17 +++
 8 files changed, 214 insertions(+), 201 deletions(-)
 create mode 100644 crates/ra_hir_def/src/adt.rs

diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs
index 4fa2062bdba..0436d20b79e 100644
--- a/crates/ra_hir/src/adt.rs
+++ b/crates/ra_hir/src/adt.rs
@@ -3,152 +3,16 @@
 
 use std::sync::Arc;
 
-use hir_def::{type_ref::TypeRef, LocalEnumVariantId};
-use hir_expand::name::AsName;
-use ra_arena::{impl_arena_id, Arena, RawId};
-use ra_syntax::ast::{self, NameOwner, StructKind, TypeAscriptionOwner};
+use hir_def::adt::VariantData;
 
 use crate::{
-    db::{AstDatabase, DefDatabase, HirDatabase},
-    Enum, EnumVariant, FieldSource, HasSource, Module, Name, Source, Struct, StructField,
+    db::{DefDatabase, HirDatabase},
+    EnumVariant, Module, Name, Struct, StructField,
 };
 
 impl Struct {
     pub(crate) fn variant_data(self, db: &impl DefDatabase) -> Arc<VariantData> {
-        db.struct_data(self).variant_data.clone()
-    }
-}
-
-/// Note that we use `StructData` for unions as well!
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct StructData {
-    pub(crate) name: Option<Name>,
-    pub(crate) variant_data: Arc<VariantData>,
-}
-
-impl StructData {
-    fn new(struct_def: &ast::StructDef) -> StructData {
-        let name = struct_def.name().map(|n| n.as_name());
-        let variant_data = VariantData::new(struct_def.kind());
-        let variant_data = Arc::new(variant_data);
-        StructData { name, variant_data }
-    }
-
-    pub(crate) fn struct_data_query(
-        db: &(impl DefDatabase + AstDatabase),
-        struct_: Struct,
-    ) -> Arc<StructData> {
-        let src = struct_.source(db);
-        Arc::new(StructData::new(&src.ast))
-    }
-}
-
-fn variants(enum_def: &ast::EnumDef) -> impl Iterator<Item = ast::EnumVariant> {
-    enum_def.variant_list().into_iter().flat_map(|it| it.variants())
-}
-
-impl EnumVariant {
-    pub(crate) fn source_impl(
-        self,
-        db: &(impl DefDatabase + AstDatabase),
-    ) -> Source<ast::EnumVariant> {
-        let src = self.parent.source(db);
-        let ast = variants(&src.ast)
-            .zip(db.enum_data(self.parent).variants.iter())
-            .find(|(_syntax, (id, _))| *id == self.id)
-            .unwrap()
-            .0;
-        Source { file_id: src.file_id, ast }
-    }
-    pub(crate) fn variant_data(self, db: &impl DefDatabase) -> Arc<VariantData> {
-        db.enum_data(self.parent).variants[self.id].variant_data.clone()
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct EnumData {
-    pub(crate) name: Option<Name>,
-    pub(crate) variants: Arena<LocalEnumVariantId, EnumVariantData>,
-}
-
-impl EnumData {
-    pub(crate) fn enum_data_query(db: &(impl DefDatabase + AstDatabase), e: Enum) -> Arc<EnumData> {
-        let src = e.source(db);
-        let name = src.ast.name().map(|n| n.as_name());
-        let variants = variants(&src.ast)
-            .map(|var| EnumVariantData {
-                name: var.name().map(|it| it.as_name()),
-                variant_data: Arc::new(VariantData::new(var.kind())),
-            })
-            .collect();
-        Arc::new(EnumData { name, variants })
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub(crate) struct EnumVariantData {
-    pub(crate) name: Option<Name>,
-    variant_data: Arc<VariantData>,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub(crate) struct StructFieldId(RawId);
-impl_arena_id!(StructFieldId);
-
-/// A single field of an enum variant or struct
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct StructFieldData {
-    pub(crate) name: Name,
-    pub(crate) type_ref: TypeRef,
-}
-
-/// Fields of an enum variant or struct
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub(crate) struct VariantData(VariantDataInner);
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-enum VariantDataInner {
-    Struct(Arena<StructFieldId, StructFieldData>),
-    Tuple(Arena<StructFieldId, StructFieldData>),
-    Unit,
-}
-
-impl VariantData {
-    pub(crate) fn fields(&self) -> Option<&Arena<StructFieldId, StructFieldData>> {
-        match &self.0 {
-            VariantDataInner::Struct(fields) | VariantDataInner::Tuple(fields) => Some(fields),
-            _ => None,
-        }
-    }
-}
-
-impl VariantData {
-    fn new(flavor: StructKind) -> Self {
-        let inner = match flavor {
-            ast::StructKind::Tuple(fl) => {
-                let fields = fl
-                    .fields()
-                    .enumerate()
-                    .map(|(i, fd)| StructFieldData {
-                        name: Name::new_tuple_field(i),
-                        type_ref: TypeRef::from_ast_opt(fd.type_ref()),
-                    })
-                    .collect();
-                VariantDataInner::Tuple(fields)
-            }
-            ast::StructKind::Named(fl) => {
-                let fields = fl
-                    .fields()
-                    .map(|fd| StructFieldData {
-                        name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
-                        type_ref: TypeRef::from_ast_opt(fd.ascribed_type()),
-                    })
-                    .collect();
-                VariantDataInner::Struct(fields)
-            }
-            ast::StructKind::Unit => VariantDataInner::Unit,
-        };
-        VariantData(inner)
+        db.struct_data(self.id).variant_data.clone()
     }
 }
 
@@ -188,35 +52,3 @@ impl VariantDef {
         }
     }
 }
-
-impl StructField {
-    pub(crate) fn source_impl(&self, db: &(impl DefDatabase + AstDatabase)) -> Source<FieldSource> {
-        let var_data = self.parent.variant_data(db);
-        let fields = var_data.fields().unwrap();
-        let ss;
-        let es;
-        let (file_id, struct_kind) = match self.parent {
-            VariantDef::Struct(s) => {
-                ss = s.source(db);
-                (ss.file_id, ss.ast.kind())
-            }
-            VariantDef::EnumVariant(e) => {
-                es = e.source(db);
-                (es.file_id, es.ast.kind())
-            }
-        };
-
-        let field_sources = match struct_kind {
-            ast::StructKind::Tuple(fl) => fl.fields().map(|it| FieldSource::Pos(it)).collect(),
-            ast::StructKind::Named(fl) => fl.fields().map(|it| FieldSource::Named(it)).collect(),
-            ast::StructKind::Unit => Vec::new(),
-        };
-        let ast = field_sources
-            .into_iter()
-            .zip(fields.iter())
-            .find(|(_syntax, (id, _))| *id == self.id)
-            .unwrap()
-            .0;
-        Source { file_id, ast }
-    }
-}
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index b32aa145ea8..ae6ef760674 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -6,16 +6,17 @@ pub(crate) mod docs;
 use std::sync::Arc;
 
 use hir_def::{
+    adt::VariantData,
     builtin_type::BuiltinType,
     type_ref::{Mutability, TypeRef},
-    CrateModuleId, LocalEnumVariantId, ModuleId,
+    CrateModuleId, LocalEnumVariantId, LocalStructFieldId, ModuleId,
 };
 use hir_expand::name::{self, AsName};
 use ra_db::{CrateId, Edition};
 use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner};
 
 use crate::{
-    adt::{StructFieldId, VariantDef},
+    adt::VariantDef,
     db::{AstDatabase, DefDatabase, HirDatabase},
     diagnostics::DiagnosticSink,
     expr::{validation::ExprValidator, Body, BodySourceMap},
@@ -250,7 +251,7 @@ impl Module {
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct StructField {
     pub(crate) parent: VariantDef,
-    pub(crate) id: StructFieldId,
+    pub(crate) id: LocalStructFieldId,
 }
 
 #[derive(Debug, PartialEq, Eq)]
@@ -288,11 +289,11 @@ impl Struct {
     }
 
     pub fn name(self, db: &impl DefDatabase) -> Option<Name> {
-        db.struct_data(self).name.clone()
+        db.struct_data(self.id).name.clone()
     }
 
     pub fn fields(self, db: &impl HirDatabase) -> Vec<StructField> {
-        db.struct_data(self)
+        db.struct_data(self.id)
             .variant_data
             .fields()
             .into_iter()
@@ -302,7 +303,7 @@ impl Struct {
     }
 
     pub fn field(self, db: &impl HirDatabase, name: &Name) -> Option<StructField> {
-        db.struct_data(self)
+        db.struct_data(self.id)
             .variant_data
             .fields()
             .into_iter()
@@ -338,7 +339,7 @@ pub struct Union {
 
 impl Union {
     pub fn name(self, db: &impl DefDatabase) -> Option<Name> {
-        db.struct_data(Struct { id: self.id }).name.clone()
+        db.struct_data(self.id).name.clone()
     }
 
     pub fn module(self, db: &impl HirDatabase) -> Module {
@@ -376,15 +377,19 @@ impl Enum {
     }
 
     pub fn name(self, db: &impl DefDatabase) -> Option<Name> {
-        db.enum_data(self).name.clone()
+        db.enum_data(self.id).name.clone()
     }
 
     pub fn variants(self, db: &impl DefDatabase) -> Vec<EnumVariant> {
-        db.enum_data(self).variants.iter().map(|(id, _)| EnumVariant { parent: self, id }).collect()
+        db.enum_data(self.id)
+            .variants
+            .iter()
+            .map(|(id, _)| EnumVariant { parent: self, id })
+            .collect()
     }
 
     pub fn variant(self, db: &impl DefDatabase, name: &Name) -> Option<EnumVariant> {
-        db.enum_data(self)
+        db.enum_data(self.id)
             .variants
             .iter()
             .find(|(_id, data)| data.name.as_ref() == Some(name))
@@ -422,7 +427,7 @@ impl EnumVariant {
     }
 
     pub fn name(self, db: &impl DefDatabase) -> Option<Name> {
-        db.enum_data(self.parent).variants[self.id].name.clone()
+        db.enum_data(self.parent.id).variants[self.id].name.clone()
     }
 
     pub fn fields(self, db: &impl HirDatabase) -> Vec<StructField> {
@@ -442,6 +447,10 @@ impl EnumVariant {
             .find(|(_id, data)| data.name == *name)
             .map(|(id, _)| StructField { parent: self.into(), id })
     }
+
+    pub(crate) fn variant_data(self, db: &impl DefDatabase) -> Arc<VariantData> {
+        db.enum_data(self.parent.id).variants[self.id].variant_data.clone()
+    }
 }
 
 /// A Data Type
diff --git a/crates/ra_hir/src/code_model/src.rs b/crates/ra_hir/src/code_model/src.rs
index 5c7f61eefb4..0f4c78df760 100644
--- a/crates/ra_hir/src/code_model/src.rs
+++ b/crates/ra_hir/src/code_model/src.rs
@@ -3,6 +3,7 @@
 use ra_syntax::ast::{self, AstNode};
 
 use crate::{
+    adt::VariantDef,
     db::{AstDatabase, DefDatabase, HirDatabase},
     ids::AstItemDef,
     Const, Either, Enum, EnumVariant, FieldSource, Function, HasBody, HirFileId, MacroDef, Module,
@@ -45,7 +46,33 @@ impl Module {
 impl HasSource for StructField {
     type Ast = FieldSource;
     fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<FieldSource> {
-        self.source_impl(db)
+        let var_data = self.parent.variant_data(db);
+        let fields = var_data.fields().unwrap();
+        let ss;
+        let es;
+        let (file_id, struct_kind) = match self.parent {
+            VariantDef::Struct(s) => {
+                ss = s.source(db);
+                (ss.file_id, ss.ast.kind())
+            }
+            VariantDef::EnumVariant(e) => {
+                es = e.source(db);
+                (es.file_id, es.ast.kind())
+            }
+        };
+
+        let field_sources = match struct_kind {
+            ast::StructKind::Tuple(fl) => fl.fields().map(|it| FieldSource::Pos(it)).collect(),
+            ast::StructKind::Named(fl) => fl.fields().map(|it| FieldSource::Named(it)).collect(),
+            ast::StructKind::Unit => Vec::new(),
+        };
+        let ast = field_sources
+            .into_iter()
+            .zip(fields.iter())
+            .find(|(_syntax, (id, _))| *id == self.id)
+            .unwrap()
+            .0;
+        Source { file_id, ast }
     }
 }
 impl HasSource for Struct {
@@ -69,7 +96,18 @@ impl HasSource for Enum {
 impl HasSource for EnumVariant {
     type Ast = ast::EnumVariant;
     fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ast::EnumVariant> {
-        self.source_impl(db)
+        let enum_data = db.enum_data(self.parent.id);
+        let src = self.parent.id.source(db);
+        let ast = src
+            .ast
+            .variant_list()
+            .into_iter()
+            .flat_map(|it| it.variants())
+            .zip(enum_data.variants.iter())
+            .find(|(_syntax, (id, _))| *id == self.id)
+            .unwrap()
+            .0;
+        Source { file_id: src.file_id, ast }
     }
 }
 impl HasSource for Function {
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index ebfd970ebc4..89ca4e39f0a 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -6,7 +6,6 @@ use ra_db::salsa;
 use ra_syntax::SmolStr;
 
 use crate::{
-    adt::{EnumData, StructData},
     debug::HirDebugDatabase,
     generics::{GenericDef, GenericParams},
     ids,
@@ -19,13 +18,13 @@ use crate::{
         InferenceResult, Substs, Ty, TypableDef, TypeCtor,
     },
     type_alias::TypeAliasData,
-    Const, ConstData, Crate, DefWithBody, Enum, ExprScopes, FnData, Function, Module, Static,
-    Struct, StructField, Trait, TypeAlias,
+    Const, ConstData, Crate, DefWithBody, ExprScopes, FnData, Function, Module, Static,
+    StructField, Trait, TypeAlias,
 };
 
 pub use hir_def::db::{
-    DefDatabase2, DefDatabase2Storage, InternDatabase, InternDatabaseStorage, RawItemsQuery,
-    RawItemsWithSourceMapQuery,
+    DefDatabase2, DefDatabase2Storage, EnumDataQuery, InternDatabase, InternDatabaseStorage,
+    RawItemsQuery, RawItemsWithSourceMapQuery, StructDataQuery,
 };
 pub use hir_expand::db::{
     AstDatabase, AstDatabaseStorage, AstIdMapQuery, MacroArgQuery, MacroDefQuery, MacroExpandQuery,
@@ -36,12 +35,6 @@ pub use hir_expand::db::{
 #[salsa::query_group(DefDatabaseStorage)]
 #[salsa::requires(AstDatabase)]
 pub trait DefDatabase: HirDebugDatabase + DefDatabase2 {
-    #[salsa::invoke(crate::adt::StructData::struct_data_query)]
-    fn struct_data(&self, s: Struct) -> Arc<StructData>;
-
-    #[salsa::invoke(crate::adt::EnumData::enum_data_query)]
-    fn enum_data(&self, e: Enum) -> Arc<EnumData>;
-
     #[salsa::invoke(crate::traits::TraitData::trait_data_query)]
     fn trait_data(&self, t: Trait) -> Arc<TraitData>;
 
diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs
index dd7cd979ff4..8e28343071a 100644
--- a/crates/ra_hir/src/ty/lower.rs
+++ b/crates/ra_hir/src/ty/lower.rs
@@ -655,8 +655,8 @@ fn type_for_builtin(def: BuiltinType) -> Ty {
 }
 
 fn fn_sig_for_struct_constructor(db: &impl HirDatabase, def: Struct) -> FnSig {
-    let var_data = def.variant_data(db);
-    let fields = match var_data.fields() {
+    let struct_data = db.struct_data(def.id);
+    let fields = match struct_data.variant_data.fields() {
         Some(fields) => fields,
         None => panic!("fn_sig_for_struct_constructor called on unit struct"),
     };
@@ -671,8 +671,8 @@ fn fn_sig_for_struct_constructor(db: &impl HirDatabase, def: Struct) -> FnSig {
 
 /// Build the type of a tuple struct constructor.
 fn type_for_struct_constructor(db: &impl HirDatabase, def: Struct) -> Ty {
-    let var_data = def.variant_data(db);
-    if var_data.fields().is_none() {
+    let struct_data = db.struct_data(def.id);
+    if struct_data.variant_data.fields().is_none() {
         return type_for_adt(db, def); // Unit struct
     }
     let generics = def.generic_params(db);
diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs
new file mode 100644
index 00000000000..22bd469f0a3
--- /dev/null
+++ b/crates/ra_hir_def/src/adt.rs
@@ -0,0 +1,114 @@
+//! Defines hir-level representation of structs, enums and unions
+
+use std::sync::Arc;
+
+use hir_expand::name::{AsName, Name};
+use ra_arena::Arena;
+use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner};
+
+use crate::{
+    db::DefDatabase2, type_ref::TypeRef, AstItemDef, EnumId, LocalEnumVariantId,
+    LocalStructFieldId, StructId,
+};
+
+/// Note that we use `StructData` for unions as well!
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct StructData {
+    pub name: Option<Name>,
+    pub variant_data: Arc<VariantData>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct EnumData {
+    pub name: Option<Name>,
+    pub variants: Arena<LocalEnumVariantId, EnumVariantData>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct EnumVariantData {
+    pub name: Option<Name>,
+    pub variant_data: Arc<VariantData>,
+}
+
+/// Fields of an enum variant or struct
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct VariantData(VariantDataInner);
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+enum VariantDataInner {
+    Struct(Arena<LocalStructFieldId, StructFieldData>),
+    Tuple(Arena<LocalStructFieldId, StructFieldData>),
+    Unit,
+}
+
+/// A single field of an enum variant or struct
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct StructFieldData {
+    pub name: Name,
+    pub type_ref: TypeRef,
+}
+
+impl StructData {
+    pub(crate) fn struct_data_query(db: &impl DefDatabase2, struct_: StructId) -> Arc<StructData> {
+        let src = struct_.source(db);
+        let name = src.ast.name().map(|n| n.as_name());
+        let variant_data = VariantData::new(src.ast.kind());
+        let variant_data = Arc::new(variant_data);
+        Arc::new(StructData { name, variant_data })
+    }
+}
+
+impl EnumData {
+    pub(crate) fn enum_data_query(db: &impl DefDatabase2, e: EnumId) -> Arc<EnumData> {
+        let src = e.source(db);
+        let name = src.ast.name().map(|n| n.as_name());
+        let variants = src
+            .ast
+            .variant_list()
+            .into_iter()
+            .flat_map(|it| it.variants())
+            .map(|var| EnumVariantData {
+                name: var.name().map(|it| it.as_name()),
+                variant_data: Arc::new(VariantData::new(var.kind())),
+            })
+            .collect();
+        Arc::new(EnumData { name, variants })
+    }
+}
+
+impl VariantData {
+    fn new(flavor: ast::StructKind) -> Self {
+        let inner = match flavor {
+            ast::StructKind::Tuple(fl) => {
+                let fields = fl
+                    .fields()
+                    .enumerate()
+                    .map(|(i, fd)| StructFieldData {
+                        name: Name::new_tuple_field(i),
+                        type_ref: TypeRef::from_ast_opt(fd.type_ref()),
+                    })
+                    .collect();
+                VariantDataInner::Tuple(fields)
+            }
+            ast::StructKind::Named(fl) => {
+                let fields = fl
+                    .fields()
+                    .map(|fd| StructFieldData {
+                        name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
+                        type_ref: TypeRef::from_ast_opt(fd.ascribed_type()),
+                    })
+                    .collect();
+                VariantDataInner::Struct(fields)
+            }
+            ast::StructKind::Unit => VariantDataInner::Unit,
+        };
+        VariantData(inner)
+    }
+
+    pub fn fields(&self) -> Option<&Arena<LocalStructFieldId, StructFieldData>> {
+        match &self.0 {
+            VariantDataInner::Struct(fields) | VariantDataInner::Tuple(fields) => Some(fields),
+            _ => None,
+        }
+    }
+}
diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs
index b271636b07e..f6027013f2a 100644
--- a/crates/ra_hir_def/src/db.rs
+++ b/crates/ra_hir_def/src/db.rs
@@ -5,7 +5,11 @@ use hir_expand::{db::AstDatabase, HirFileId};
 use ra_db::{salsa, SourceDatabase};
 use ra_syntax::ast;
 
-use crate::nameres::raw::{ImportSourceMap, RawItems};
+use crate::{
+    adt::{EnumData, StructData},
+    nameres::raw::{ImportSourceMap, RawItems},
+    EnumId, StructId,
+};
 
 #[salsa::query_group(InternDatabaseStorage)]
 pub trait InternDatabase: SourceDatabase {
@@ -37,4 +41,10 @@ pub trait DefDatabase2: InternDatabase + AstDatabase {
 
     #[salsa::invoke(RawItems::raw_items_query)]
     fn raw_items(&self, file_id: HirFileId) -> Arc<RawItems>;
+
+    #[salsa::invoke(StructData::struct_data_query)]
+    fn struct_data(&self, s: StructId) -> Arc<StructData>;
+
+    #[salsa::invoke(EnumData::enum_data_query)]
+    fn enum_data(&self, e: EnumId) -> Arc<EnumData>;
 }
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs
index 93ad40005c7..76d5f18522b 100644
--- a/crates/ra_hir_def/src/lib.rs
+++ b/crates/ra_hir_def/src/lib.rs
@@ -12,6 +12,7 @@ pub mod attr;
 pub mod path;
 pub mod type_ref;
 pub mod builtin_type;
+pub mod adt;
 
 // FIXME: this should be private
 pub mod nameres;
@@ -259,6 +260,22 @@ pub struct EnumVariantId {
 pub struct LocalEnumVariantId(RawId);
 impl_arena_id!(LocalEnumVariantId);
 
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum VariantId {
+    EnumVariantId(EnumVariantId),
+    StructId(StructId),
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct StructFieldId {
+    parent: VariantId,
+    local_id: LocalStructFieldId,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct LocalStructFieldId(RawId);
+impl_arena_id!(LocalStructFieldId);
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct ConstId(salsa::InternId);
 impl_intern_key!(ConstId);