use cairo_lang_debug::debug::DebugWithDb;
use cairo_lang_filesystem::ids::CrateId;
pub use cairo_lang_filesystem::ids::UnstableSalsaId;
use cairo_lang_syntax::node::ast::TerminalIdentifierGreen;
use cairo_lang_syntax::node::db::SyntaxGroup;
use cairo_lang_syntax::node::helpers::{GetIdentifier, NameGreen};
use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
use cairo_lang_syntax::node::kind::SyntaxKind;
use cairo_lang_syntax::node::stable_ptr::SyntaxStablePtr;
use cairo_lang_syntax::node::{ast, Terminal, TypedSyntaxNode};
use cairo_lang_utils::{define_short_id, OptionFrom};
use salsa;
use smol_str::SmolStr;
use crate::db::DefsGroup;
use crate::diagnostic_utils::StableLocation;
pub trait LanguageElementId {
    fn module_file_id(&self, db: &dyn DefsGroup) -> ModuleFileId;
    fn untyped_stable_ptr(&self, db: &dyn DefsGroup) -> SyntaxStablePtrId;
    fn parent_module(&self, db: &dyn DefsGroup) -> ModuleId {
        self.module_file_id(db).0
    }
    fn file_index(&self, db: &dyn DefsGroup) -> FileIndex {
        self.module_file_id(db).1
    }
    fn stable_location(&self, db: &dyn DefsGroup) -> StableLocation;
}
pub trait TopLevelLanguageElementId: LanguageElementId {
    fn name(&self, db: &dyn DefsGroup) -> SmolStr;
    fn full_path(&self, db: &dyn DefsGroup) -> String {
        format!("{}::{}", self.parent_module(db).full_path(db), self.name(db))
    }
}
macro_rules! define_language_element_id {
    ($short_id:ident, $long_id:ident, $ast_ty:ty, $lookup:ident $(, $name:ident)?) => {
        define_language_element_id_partial!($short_id, $long_id, $ast_ty, $lookup $(, $name)?);
        impl_top_level_language_element_id!($short_id, $lookup $(, $name)?);
    };
}
macro_rules! define_language_element_id_partial {
    ($short_id:ident, $long_id:ident, $ast_ty:ty, $lookup:ident $(,$name:ident)?) => {
        #[derive(Clone, PartialEq, Eq, Hash, Debug)]
        pub struct $long_id(pub ModuleFileId, pub <$ast_ty as TypedSyntaxNode>::StablePtr);
        $(
            impl $long_id {
                pub fn $name(&self, db: &dyn DefsGroup) -> SmolStr {
                    let syntax_db = db.upcast();
                    let terminal_green = self.1.name_green(syntax_db);
                    terminal_green.identifier(syntax_db)
                }
            }
            impl<'a, T: ?Sized + cairo_lang_utils::Upcast<dyn DefsGroup + 'a>> cairo_lang_debug::DebugWithDb<T>
                for $long_id
            {
                fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &T) -> std::fmt::Result {
                    let db: &(dyn DefsGroup + 'a) = db.upcast();
                    let $long_id(module_file_id, _stable_ptr) = self;
                    write!(
                        f,
                        "{}({}::{})",
                        stringify!($short_id),
                        module_file_id.0.full_path(db),
                        self.name(db)
                    )
                }
            }
        )?
        define_short_id!($short_id, $long_id, DefsGroup, $lookup);
        impl $short_id {
            pub fn stable_ptr(self, db: &dyn DefsGroup) -> <$ast_ty as TypedSyntaxNode>::StablePtr {
                db.$lookup(self).1
            }
            $(
                pub fn $name(&self, db: &dyn DefsGroup) -> SmolStr {
                    db.$lookup(*self).name(db)
                }
            )?
        }
        impl LanguageElementId for $short_id {
            fn module_file_id(&self, db: &dyn DefsGroup) -> ModuleFileId {
                db.$lookup(*self).0
            }
            fn untyped_stable_ptr(&self, db: &dyn DefsGroup) -> SyntaxStablePtrId {
                self.stable_ptr(db).untyped()
            }
            fn stable_location(&self, db: &dyn DefsGroup) -> StableLocation {
                let $long_id(module_file_id, stable_ptr) = db.$lookup(*self);
                StableLocation { module_file_id, stable_ptr: stable_ptr.untyped() }
            }
        }
    };
}
macro_rules! impl_top_level_language_element_id {
    ($short_id:ident, $lookup:ident $(,$name:ident)?) => {
        $(
            impl TopLevelLanguageElementId for $short_id {
                fn $name(&self, db: &dyn DefsGroup) -> SmolStr {
                    db.$lookup(*self).name(db)
                }
            }
        )?
    };
}
macro_rules! define_language_element_id_as_enum {
    (
        #[toplevel]
        $(#[doc = $doc:expr])*
        pub enum $enum_name:ident {
            $($variant:ident ($variant_ty:ty),)*
        }
    ) => {
        toplevel_enum! {
            pub enum $enum_name {
                $($variant($variant_ty),)*
            }
        }
        define_language_element_id_as_enum! {
            $(#[doc = $doc])*
            pub enum $enum_name {
                $($variant($variant_ty),)*
            }
        }
    };
    (
        $(#[doc = $doc:expr])*
        pub enum $enum_name:ident {
            $($variant:ident ($variant_ty:ty),)*
        }
    ) => {
        $(#[doc = $doc])*
        #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
        pub enum $enum_name {
            $($variant($variant_ty),)*
        }
        impl<T: ?Sized + cairo_lang_utils::Upcast<dyn DefsGroup + 'static>> cairo_lang_debug::DebugWithDb<T>
            for $enum_name
        {
            fn fmt(
                &self,
                f: &mut std::fmt::Formatter<'_>,
                db: &T,
            ) -> std::fmt::Result {
                let db : &(dyn DefsGroup + 'static) = db.upcast();
                match self {
                    $(
                        $enum_name::$variant(id) => id.fmt(f, db),
                    )*
                }
            }
        }
        impl LanguageElementId for $enum_name {
            fn module_file_id(&self, db: &dyn DefsGroup) -> ModuleFileId {
                match self {
                    $(
                        $enum_name::$variant(id) => id.module_file_id(db),
                    )*
                }
            }
            fn untyped_stable_ptr(&self, db: &dyn DefsGroup) -> SyntaxStablePtrId {
                match self {
                    $(
                        $enum_name::$variant(id) => id.untyped_stable_ptr(db),
                    )*
                }
            }
            fn stable_location(&self, db: &dyn DefsGroup) -> StableLocation {
                 match self {
                    $(
                        $enum_name::$variant(id) => id.stable_location(db),
                    )*
                }
            }
        }
        $(
            impl OptionFrom<$enum_name> for $variant_ty {
                fn option_from(other: $enum_name) -> Option<Self> {
                    if let $enum_name::$variant(id) = other {
                        Some(id)
                    } else {
                        None
                    }
                }
            }
        )*
    }
}
macro_rules! toplevel_enum {
    (
        pub enum $enum_name:ident {
            $($variant:ident ($variant_ty:ty),)*
        }
    ) => {
        impl TopLevelLanguageElementId for $enum_name {
            fn name(&self, db: &dyn DefsGroup) -> SmolStr {
                match self {
                    $(
                        $enum_name::$variant(id) => id.name(db),
                    )*
                }
            }
        }
    }
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum ModuleId {
    CrateRoot(CrateId),
    Submodule(SubmoduleId),
}
impl ModuleId {
    pub fn full_path(&self, db: &dyn DefsGroup) -> String {
        match self {
            ModuleId::CrateRoot(id) => db.lookup_intern_crate(*id).0.to_string(),
            ModuleId::Submodule(id) => {
                format!("{}::{}", id.parent_module(db).full_path(db), id.name(db))
            }
        }
    }
    pub fn owning_crate(&self, db: &dyn DefsGroup) -> CrateId {
        match self {
            ModuleId::CrateRoot(crate_id) => *crate_id,
            ModuleId::Submodule(submodule) => submodule.parent_module(db).owning_crate(db),
        }
    }
}
impl DebugWithDb<dyn DefsGroup> for ModuleId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &dyn DefsGroup) -> std::fmt::Result {
        write!(f, "ModuleId({})", self.full_path(db))
    }
}
#[derive(Copy, Clone, Debug, Default, Hash, PartialEq, Eq)]
pub struct FileIndex(pub usize);
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct ModuleFileId(pub ModuleId, pub FileIndex);
define_language_element_id_as_enum! {
    pub enum ModuleItemId {
        Constant(ConstantId),
        Submodule(SubmoduleId),
        Use(UseId),
        FreeFunction(FreeFunctionId),
        Struct(StructId),
        Enum(EnumId),
        TypeAlias(TypeAliasId),
        ImplAlias(ImplAliasId),
        Trait(TraitId),
        Impl(ImplDefId),
        ExternType(ExternTypeId),
        ExternFunction(ExternFunctionId),
    }
}
define_language_element_id!(
    SubmoduleId,
    SubmoduleLongId,
    ast::ItemModule,
    lookup_intern_submodule,
    name
);
impl UnstableSalsaId for SubmoduleId {
    fn get_internal_id(&self) -> &salsa::InternId {
        &self.0
    }
}
define_language_element_id!(
    ConstantId,
    ConstantLongId,
    ast::ItemConstant,
    lookup_intern_constant,
    name
);
define_language_element_id!(UseId, UseLongId, ast::UsePathLeaf, lookup_intern_use, name);
define_language_element_id!(
    FreeFunctionId,
    FreeFunctionLongId,
    ast::FunctionWithBody,
    lookup_intern_free_function,
    name
);
impl UnstableSalsaId for FreeFunctionId {
    fn get_internal_id(&self) -> &salsa::InternId {
        &self.0
    }
}
define_language_element_id!(ImplDefId, ImplDefLongId, ast::ItemImpl, lookup_intern_impl, name);
define_language_element_id_partial!(
    ImplFunctionId,
    ImplFunctionLongId,
    ast::FunctionWithBody,
    lookup_intern_impl_function,
    name
);
impl ImplFunctionId {
    pub fn impl_def_id(&self, db: &dyn DefsGroup) -> ImplDefId {
        let ImplFunctionLongId(module_file_id, ptr) = db.lookup_intern_impl_function(*self);
        let SyntaxStablePtr::Child { parent, .. } = db.lookup_intern_stable_ptr(ptr.untyped())
        else {
            panic!()
        };
        let SyntaxStablePtr::Child { parent, .. } = db.lookup_intern_stable_ptr(parent) else {
            panic!()
        };
        let SyntaxStablePtr::Child { parent, .. } = db.lookup_intern_stable_ptr(parent) else {
            panic!()
        };
        let impl_ptr = ast::ItemImplPtr(parent);
        db.intern_impl(ImplDefLongId(module_file_id, impl_ptr))
    }
}
impl UnstableSalsaId for ImplFunctionId {
    fn get_internal_id(&self) -> &salsa::InternId {
        &self.0
    }
}
impl TopLevelLanguageElementId for ImplFunctionId {
    fn full_path(&self, db: &dyn DefsGroup) -> String {
        format!("{}::{}", self.impl_def_id(db).name(db), self.name(db))
    }
    fn name(&self, db: &dyn DefsGroup) -> SmolStr {
        db.lookup_intern_impl_function(*self).name(db)
    }
}
define_language_element_id_as_enum! {
    pub enum FunctionWithBodyId {
        Free(FreeFunctionId),
        Impl(ImplFunctionId),
    }
}
impl FunctionWithBodyId {
    pub fn name(&self, db: &dyn DefsGroup) -> SmolStr {
        match self {
            FunctionWithBodyId::Free(free_function) => free_function.name(db),
            FunctionWithBodyId::Impl(impl_function) => impl_function.name(db),
        }
    }
}
impl TopLevelLanguageElementId for FunctionWithBodyId {
    fn name(&self, db: &dyn DefsGroup) -> SmolStr {
        match self {
            FunctionWithBodyId::Free(free_function_id) => {
                db.lookup_intern_free_function(*free_function_id).name(db)
            }
            FunctionWithBodyId::Impl(impl_function_id) => {
                db.lookup_intern_impl_function(*impl_function_id).name(db)
            }
        }
    }
}
define_language_element_id!(
    ExternFunctionId,
    ExternFunctionLongId,
    ast::ItemExternFunction,
    lookup_intern_extern_function,
    name
);
define_language_element_id!(StructId, StructLongId, ast::ItemStruct, lookup_intern_struct, name);
define_language_element_id!(EnumId, EnumLongId, ast::ItemEnum, lookup_intern_enum, name);
define_language_element_id!(
    TypeAliasId,
    TypeAliasLongId,
    ast::ItemTypeAlias,
    lookup_intern_type_alias,
    name
);
define_language_element_id!(
    ImplAliasId,
    ImplAliasLongId,
    ast::ItemImplAlias,
    lookup_intern_impl_alias,
    name
);
define_language_element_id!(
    ExternTypeId,
    ExternTypeLongId,
    ast::ItemExternType,
    lookup_intern_extern_type,
    name
);
define_language_element_id!(TraitId, TraitLongId, ast::ItemTrait, lookup_intern_trait, name);
define_language_element_id_partial!(
    TraitFunctionId,
    TraitFunctionLongId,
    ast::TraitItemFunction,
    lookup_intern_trait_function,
    name
);
impl TraitFunctionId {
    pub fn trait_id(&self, db: &dyn DefsGroup) -> TraitId {
        let TraitFunctionLongId(module_file_id, ptr) = db.lookup_intern_trait_function(*self);
        let SyntaxStablePtr::Child { parent, .. } = db.lookup_intern_stable_ptr(ptr.untyped())
        else {
            panic!()
        };
        let SyntaxStablePtr::Child { parent, .. } = db.lookup_intern_stable_ptr(parent) else {
            panic!()
        };
        let SyntaxStablePtr::Child { parent, .. } = db.lookup_intern_stable_ptr(parent) else {
            panic!()
        };
        let trait_ptr = ast::ItemTraitPtr(parent);
        db.intern_trait(TraitLongId(module_file_id, trait_ptr))
    }
}
impl TopLevelLanguageElementId for TraitFunctionId {
    fn full_path(&self, db: &dyn DefsGroup) -> String {
        format!("{}::{}", self.trait_id(db).name(db), self.name(db))
    }
    fn name(&self, db: &dyn DefsGroup) -> SmolStr {
        db.lookup_intern_trait_function(*self).name(db)
    }
}
define_language_element_id!(MemberId, MemberLongId, ast::Member, lookup_intern_member, name);
define_language_element_id!(VariantId, VariantLongId, ast::Variant, lookup_intern_variant, name);
define_language_element_id_as_enum! {
    pub enum VarId {
        Param(ParamId),
        Local(LocalVarId),
        }
}
define_language_element_id!(ParamId, ParamLongId, ast::Param, lookup_intern_param, name);
define_language_element_id!(
    GenericParamId,
    GenericParamLongId,
    ast::GenericParam,
    lookup_intern_generic_param
);
impl GenericParamLongId {
    pub fn name(&self, db: &dyn SyntaxGroup) -> SmolStr {
        let SyntaxStablePtr::Child { key_fields, .. } = db.lookup_intern_stable_ptr(self.1.0)
        else {
            unreachable!()
        };
        let name_green = TerminalIdentifierGreen(key_fields[0]);
        name_green.identifier(db)
    }
    pub fn kind(&self, db: &dyn SyntaxGroup) -> GenericKind {
        let SyntaxStablePtr::Child { kind, .. } = db.lookup_intern_stable_ptr(self.1.0) else {
            unreachable!()
        };
        match kind {
            SyntaxKind::GenericParamType => GenericKind::Type,
            SyntaxKind::GenericParamConst => GenericKind::Const,
            SyntaxKind::GenericParamImpl => GenericKind::Impl,
            _ => unreachable!(),
        }
    }
    pub fn generic_item(&self, db: &dyn DefsGroup) -> GenericItemId {
        let SyntaxStablePtr::Child { parent, .. } = db.lookup_intern_stable_ptr(self.1.0) else {
            panic!()
        };
        let SyntaxStablePtr::Child { parent, .. } = db.lookup_intern_stable_ptr(parent) else {
            panic!()
        };
        let SyntaxStablePtr::Child { parent, .. } = db.lookup_intern_stable_ptr(parent) else {
            panic!()
        };
        GenericItemId::from_ptr(db, self.0, parent)
    }
}
impl GenericParamId {
    pub fn name(&self, db: &dyn DefsGroup) -> SmolStr {
        db.lookup_intern_generic_param(*self).name(db.upcast())
    }
    pub fn kind(&self, db: &dyn DefsGroup) -> GenericKind {
        db.lookup_intern_generic_param(*self).kind(db.upcast())
    }
    pub fn generic_item(&self, db: &dyn DefsGroup) -> GenericItemId {
        db.lookup_intern_generic_param(*self).generic_item(db.upcast())
    }
}
impl DebugWithDb<dyn DefsGroup> for GenericParamLongId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &dyn DefsGroup) -> std::fmt::Result {
        write!(
            f,
            "GenericParam{}({}::{})",
            self.kind(db.upcast()),
            self.generic_item(db).full_path(db),
            self.name(db.upcast())
        )
    }
}
define_language_element_id_as_enum! {
    #[toplevel]
    pub enum GenericItemId {
        FreeFunc(FreeFunctionId),
        ExternFunc(ExternFunctionId),
        TraitFunc(TraitFunctionId),
        ImplFunc(ImplFunctionId),
        Trait(TraitId),
        Impl(ImplDefId),
        Struct(StructId),
        Enum(EnumId),
        ExternType(ExternTypeId),
        TypeAlias(TypeAliasId),
        ImplAlias(ImplAliasId),
    }
}
impl GenericItemId {
    pub fn from_ptr(
        db: &dyn DefsGroup,
        module_file: ModuleFileId,
        stable_ptr: SyntaxStablePtrId,
    ) -> Self {
        let SyntaxStablePtr::Child { parent: parent0, kind, .. } =
            db.lookup_intern_stable_ptr(stable_ptr)
        else {
            panic!()
        };
        match kind {
            SyntaxKind::FunctionDeclaration => {
                let SyntaxStablePtr::Child { parent: parent1, kind, .. } =
                    db.lookup_intern_stable_ptr(parent0)
                else {
                    panic!()
                };
                match kind {
                    SyntaxKind::FunctionWithBody => {
                        let SyntaxStablePtr::Child { parent: parent2, .. } =
                            db.lookup_intern_stable_ptr(parent1)
                        else {
                            panic!()
                        };
                        match db.lookup_intern_stable_ptr(parent2) {
                            SyntaxStablePtr::Root => GenericItemId::FreeFunc(
                                db.intern_free_function(FreeFunctionLongId(
                                    module_file,
                                    ast::FunctionWithBodyPtr(parent0),
                                )),
                            ),
                            SyntaxStablePtr::Child { kind, .. } => match kind {
                                SyntaxKind::ModuleBody => GenericItemId::FreeFunc(
                                    db.intern_free_function(FreeFunctionLongId(
                                        module_file,
                                        ast::FunctionWithBodyPtr(parent0),
                                    )),
                                ),
                                SyntaxKind::ImplBody => GenericItemId::ImplFunc(
                                    db.intern_impl_function(ImplFunctionLongId(
                                        module_file,
                                        ast::FunctionWithBodyPtr(parent0),
                                    )),
                                ),
                                _ => panic!(),
                            },
                        }
                    }
                    SyntaxKind::ItemExternFunction => {
                        GenericItemId::ExternFunc(db.intern_extern_function(ExternFunctionLongId(
                            module_file,
                            ast::ItemExternFunctionPtr(parent0),
                        )))
                    }
                    SyntaxKind::TraitItemFunction => {
                        GenericItemId::TraitFunc(db.intern_trait_function(TraitFunctionLongId(
                            module_file,
                            ast::TraitItemFunctionPtr(parent0),
                        )))
                    }
                    _ => panic!(),
                }
            }
            SyntaxKind::ItemImpl => GenericItemId::Impl(
                db.intern_impl(ImplDefLongId(module_file, ast::ItemImplPtr(stable_ptr))),
            ),
            SyntaxKind::ItemTrait => GenericItemId::Trait(
                db.intern_trait(TraitLongId(module_file, ast::ItemTraitPtr(stable_ptr))),
            ),
            SyntaxKind::ItemStruct => GenericItemId::Struct(
                db.intern_struct(StructLongId(module_file, ast::ItemStructPtr(stable_ptr))),
            ),
            SyntaxKind::ItemEnum => GenericItemId::Enum(
                db.intern_enum(EnumLongId(module_file, ast::ItemEnumPtr(stable_ptr))),
            ),
            SyntaxKind::ItemExternType => GenericItemId::ExternType(db.intern_extern_type(
                ExternTypeLongId(module_file, ast::ItemExternTypePtr(stable_ptr)),
            )),
            SyntaxKind::ItemTypeAlias => GenericItemId::TypeAlias(db.intern_type_alias(
                TypeAliasLongId(module_file, ast::ItemTypeAliasPtr(stable_ptr)),
            )),
            SyntaxKind::ItemImplAlias => GenericItemId::ImplAlias(db.intern_impl_alias(
                ImplAliasLongId(module_file, ast::ItemImplAliasPtr(stable_ptr)),
            )),
            _ => panic!(),
        }
    }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum GenericKind {
    Type,
    Const,
    Impl,
}
impl std::fmt::Display for GenericKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            GenericKind::Type => write!(f, "Type"),
            GenericKind::Const => write!(f, "Const"),
            GenericKind::Impl => write!(f, "Impl"),
        }
    }
}
define_language_element_id!(
    LocalVarId,
    LocalVarLongId,
    ast::TerminalIdentifier,
    lookup_intern_local_var
);
impl DebugWithDb<dyn DefsGroup> for LocalVarLongId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &dyn DefsGroup) -> std::fmt::Result {
        let syntax_db = db.upcast();
        let LocalVarLongId(module_file_id, ptr) = self;
        let file_id = db.module_file(*module_file_id).map_err(|_| std::fmt::Error)?;
        let root = db.file_syntax(file_id).map_err(|_| std::fmt::Error)?;
        let text = ast::TerminalIdentifier::from_ptr(syntax_db, &root, *ptr).text(syntax_db);
        write!(f, "LocalVarId({}::{})", module_file_id.0.full_path(db), text)
    }
}
define_language_element_id_as_enum! {
    #[toplevel]
    pub enum FunctionTitleId {
        Free(FreeFunctionId),
        Extern(ExternFunctionId),
        Trait(TraitFunctionId),
        Impl(ImplFunctionId),
    }
}
impl FunctionTitleId {
    pub fn format(&self, db: &dyn DefsGroup) -> String {
        let function_name = match *self {
            FunctionTitleId::Free(_) | FunctionTitleId::Extern(_) => self.name(db).into(),
            FunctionTitleId::Trait(id) => id.full_path(db),
            FunctionTitleId::Impl(id) => id.full_path(db),
        };
        format!("{}::{}", self.parent_module(db).full_path(db), function_name)
    }
}
define_language_element_id_as_enum! {
    #[toplevel]
    pub enum GenericTypeId {
        Struct(StructId),
        Enum(EnumId),
        Extern(ExternTypeId),
        }
}
impl GenericTypeId {
    pub fn format(&self, db: &dyn DefsGroup) -> String {
        format!("{}::{}", self.parent_module(db).full_path(db), self.name(db))
    }
}
impl OptionFrom<ModuleItemId> for GenericTypeId {
    fn option_from(item: ModuleItemId) -> Option<Self> {
        match item {
            ModuleItemId::Struct(id) => Some(GenericTypeId::Struct(id)),
            ModuleItemId::Enum(id) => Some(GenericTypeId::Enum(id)),
            ModuleItemId::ExternType(id) => Some(GenericTypeId::Extern(id)),
            ModuleItemId::Constant(_)
            | ModuleItemId::Submodule(_)
            | ModuleItemId::TypeAlias(_)
            | ModuleItemId::ImplAlias(_)
            | ModuleItemId::Use(_)
            | ModuleItemId::FreeFunction(_)
            | ModuleItemId::Trait(_)
            | ModuleItemId::Impl(_)
            | ModuleItemId::ExternFunction(_) => None,
        }
    }
}
impl From<GenericItemId> for LookupItemId {
    fn from(item: GenericItemId) -> Self {
        match item {
            GenericItemId::FreeFunc(id) => LookupItemId::ModuleItem(ModuleItemId::FreeFunction(id)),
            GenericItemId::ExternFunc(id) => {
                LookupItemId::ModuleItem(ModuleItemId::ExternFunction(id))
            }
            GenericItemId::TraitFunc(id) => LookupItemId::TraitFunction(id),
            GenericItemId::ImplFunc(id) => LookupItemId::ImplFunction(id),
            GenericItemId::Trait(id) => LookupItemId::ModuleItem(ModuleItemId::Trait(id)),
            GenericItemId::Impl(id) => LookupItemId::ModuleItem(ModuleItemId::Impl(id)),
            GenericItemId::Struct(id) => LookupItemId::ModuleItem(ModuleItemId::Struct(id)),
            GenericItemId::Enum(id) => LookupItemId::ModuleItem(ModuleItemId::Enum(id)),
            GenericItemId::ExternType(id) => LookupItemId::ModuleItem(ModuleItemId::ExternType(id)),
            GenericItemId::TypeAlias(id) => LookupItemId::ModuleItem(ModuleItemId::TypeAlias(id)),
            GenericItemId::ImplAlias(id) => LookupItemId::ModuleItem(ModuleItemId::ImplAlias(id)),
        }
    }
}
define_language_element_id_as_enum! {
    pub enum LookupItemId {
        ModuleItem(ModuleItemId),
        ImplFunction(ImplFunctionId),
        TraitFunction(TraitFunctionId),
    }
}