use std::collections::HashSet;
use cairo_lang_defs::ids::TraitItemId::Function;
use cairo_lang_defs::ids::{
ConstantId, EnumId, ExternFunctionId, ExternTypeId, FreeFunctionId, GenericParamId,
ImplAliasId, ImplConstantDefId, ImplDefId, ImplFunctionId, ImplImplDefId, ImplItemId,
ImplTypeDefId, LanguageElementId, LookupItemId, MemberId, ModuleItemId, ModuleTypeAliasId,
NamedLanguageElementId, StructId, TopLevelLanguageElementId, TraitConstantId, TraitFunctionId,
TraitId, TraitItemId, TraitTypeId,
};
use cairo_lang_filesystem::ids::SmolStrId;
use cairo_lang_semantic::items::constant::ConstantSemantic;
use cairo_lang_semantic::items::enm::EnumSemantic;
use cairo_lang_semantic::items::extern_function::ExternFunctionSemantic;
use cairo_lang_semantic::items::extern_type::ExternTypeSemantic;
use cairo_lang_semantic::items::free_function::FreeFunctionSemantic;
use cairo_lang_semantic::items::imp::ImplSemantic;
use cairo_lang_semantic::items::impl_alias::ImplAliasSemantic;
use cairo_lang_semantic::items::module::{ModuleItemInfo, ModuleSemantic};
use cairo_lang_semantic::items::module_type_alias::ModuleTypeAliasSemantic;
use cairo_lang_semantic::items::structure::StructSemantic;
use cairo_lang_semantic::items::trt::TraitSemantic;
use cairo_lang_semantic::items::visibility::Visibility;
use cairo_lang_semantic::lookup_item::HasResolverData;
use cairo_lang_semantic::{Expr, GenericArgumentId, GenericParam, Parameter, TypeId};
use cairo_lang_syntax::attribute::structured::Attribute;
use itertools::Itertools;
use salsa::Database;
use crate::documentable_item::DocumentableItemId;
use crate::documentable_item::DocumentableItemId::Member;
use crate::signature_errors::SignatureError;
pub struct DocumentableItemSignatureData<'db> {
pub item_id: DocumentableItemId<'db>,
pub name: SmolStrId<'db>,
pub visibility: Visibility,
pub generic_args: Option<Vec<GenericArgumentId<'db>>>,
pub generic_params: Option<Vec<GenericParam<'db>>>,
pub variants: Option<Vec<(SmolStrId<'db>, TypeId<'db>)>>,
pub members: Option<Vec<(SmolStrId<'db>, TypeId<'db>, Visibility)>>,
pub return_type: Option<TypeId<'db>>,
pub attributes: Option<Vec<Attribute<'db>>>,
pub params: Option<Vec<Parameter<'db>>>,
pub resolver_generic_params: Option<Vec<GenericParamId<'db>>>,
pub return_value_expr: Option<Expr<'db>>,
pub full_path: String,
}
pub trait SignatureDataRetriever<'db, T> {
fn retrieve_signature_data(
db: &'db dyn Database,
item_id: T,
) -> Result<DocumentableItemSignatureData<'db>, SignatureError>;
}
macro_rules! implement_signature_data_retriever {
($t:ty, $function:expr) => {
impl<'db> SignatureDataRetriever<'db, $t> for $t {
fn retrieve_signature_data(
db: &'db dyn Database,
item_id: $t,
) -> Result<DocumentableItemSignatureData<'db>, SignatureError> {
$function(db, item_id)
}
}
};
}
implement_signature_data_retriever!(ExternTypeId<'db>, get_extern_type_full_signature);
implement_signature_data_retriever!(ConstantId<'db>, get_constant_signature_data);
implement_signature_data_retriever!(EnumId<'db>, get_enum_signature_data);
implement_signature_data_retriever!(FreeFunctionId<'db>, get_free_function_signature_data);
implement_signature_data_retriever!(ImplFunctionId<'db>, get_impl_function_signature_data);
implement_signature_data_retriever!(MemberId<'db>, get_member_signature_data);
implement_signature_data_retriever!(StructId<'db>, get_struct_signature_data);
implement_signature_data_retriever!(TraitFunctionId<'db>, get_trait_function_signature_data);
implement_signature_data_retriever!(TraitId<'db>, get_trait_signature_data);
implement_signature_data_retriever!(ImplTypeDefId<'db>, get_impl_type_def_full_signature);
implement_signature_data_retriever!(ModuleTypeAliasId<'db>, get_module_type_alias_full_signature);
implement_signature_data_retriever!(TraitConstantId<'db>, get_trait_const_signature_data);
implement_signature_data_retriever!(TraitTypeId<'db>, get_trait_type_full_signature);
implement_signature_data_retriever!(ImplDefId<'db>, get_impl_def_signature_data);
implement_signature_data_retriever!(ImplAliasId<'db>, get_impl_alias_signature_data);
implement_signature_data_retriever!(ExternFunctionId<'db>, get_extern_function_full_signature);
implement_signature_data_retriever!(ImplConstantDefId<'db>, get_impl_constant_signature_data);
implement_signature_data_retriever!(ImplImplDefId<'db>, get_impl_impl_def_signature_data);
fn get_module_item_info<'db>(
db: &'db dyn Database,
module_item_id: ModuleItemId<'db>,
) -> Result<ModuleItemInfo<'db>, SignatureError> {
let parent_module = module_item_id.parent_module(db);
let item_name = module_item_id.name(db);
db.module_item_info_by_name(parent_module, item_name)?
.ok_or(SignatureError::FailedRetrievingSemanticData(module_item_id.full_path(db)))
}
fn get_enum_signature_data<'db>(
db: &'db dyn Database,
item_id: EnumId<'db>,
) -> Result<DocumentableItemSignatureData<'db>, SignatureError> {
let module_item_id = ModuleItemId::Enum(item_id);
let module_item_info = get_module_item_info(db, module_item_id)?;
let enum_variants = db.enum_variants(item_id)?;
let mut variants = Vec::new();
for (name, variant_id) in enum_variants.iter() {
let variant_semantic = db.variant_semantic(item_id, *variant_id)?;
variants.push((*name, variant_semantic.ty));
}
let generic_params = db.enum_generic_params(item_id)?;
Ok(DocumentableItemSignatureData {
item_id: DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::Enum(item_id))),
name: item_id.name(db),
visibility: module_item_info.visibility,
generic_args: None,
generic_params: Some(generic_params.to_vec()),
variants: Some(variants),
members: None,
return_type: None,
attributes: None,
params: None,
resolver_generic_params: None,
return_value_expr: None,
full_path: item_id.full_path(db),
})
}
fn get_struct_signature_data<'db>(
db: &'db dyn Database,
item_id: StructId<'db>,
) -> Result<DocumentableItemSignatureData<'db>, SignatureError> {
let module_item_id = ModuleItemId::Struct(item_id);
let module_item_info = get_module_item_info(db, module_item_id)?;
let struct_attributes = db.struct_attributes(item_id)?;
let members = db
.struct_members(item_id)?
.iter()
.map(|(name, member)| (*name, member.ty, member.visibility))
.collect();
let generic_params = db.struct_generic_params(item_id)?;
Ok(DocumentableItemSignatureData {
item_id: DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::Struct(item_id))),
name: item_id.name(db),
visibility: module_item_info.visibility,
generic_args: None,
generic_params: Some(generic_params.to_vec()),
variants: None,
members: Some(members),
return_type: None,
attributes: Some(struct_attributes.to_vec()),
params: None,
resolver_generic_params: None,
return_value_expr: None,
full_path: item_id.full_path(db),
})
}
fn get_member_signature_data<'db>(
db: &'db dyn Database,
item_id: MemberId<'db>,
) -> Result<DocumentableItemSignatureData<'db>, SignatureError> {
let name = item_id.name(db);
let struct_id = item_id.struct_id(db);
if let Some(member) = db.struct_members(struct_id)?.get(&name) {
Ok(DocumentableItemSignatureData {
item_id: Member(item_id),
name,
visibility: member.visibility,
generic_args: None,
generic_params: None,
variants: None,
members: None,
return_type: Some(member.ty),
attributes: None,
params: None,
resolver_generic_params: None,
return_value_expr: None,
full_path: item_id.full_path(db),
})
} else {
Err(SignatureError::FailedRetrievingSemanticData(item_id.full_path(db)))
}
}
fn get_free_function_signature_data<'db>(
db: &'db dyn Database,
item_id: FreeFunctionId<'db>,
) -> Result<DocumentableItemSignatureData<'db>, SignatureError> {
let module_item_id = ModuleItemId::FreeFunction(item_id);
let module_item_info = get_module_item_info(db, module_item_id)?;
let generic_params = db.free_function_generic_params(item_id)?;
let signature = db.free_function_signature(item_id)?;
let resolver_data = db.free_function_declaration_resolver_data(item_id)?;
Ok(DocumentableItemSignatureData {
item_id: DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::FreeFunction(
item_id,
))),
name: item_id.name(db),
visibility: module_item_info.visibility,
generic_args: None,
generic_params: Some(generic_params.to_vec()),
variants: None,
members: None,
return_type: Some(signature.return_type),
attributes: None,
params: Some(signature.params.clone()),
resolver_generic_params: Some(resolver_data.generic_params.clone()),
return_value_expr: None,
full_path: item_id.full_path(db),
})
}
fn get_trait_function_signature_data<'db>(
db: &'db dyn Database,
item_id: TraitFunctionId<'db>,
) -> Result<DocumentableItemSignatureData<'db>, SignatureError> {
let signature = db.trait_function_signature(item_id)?;
let generic_params = db.trait_function_generic_params(item_id)?;
let resolver_data = db.trait_function_resolver_data(item_id)?;
let trait_id = item_id.trait_id(db);
let trait_resolver_data = trait_id.resolver_data(db)?;
let trait_params_set: HashSet<_> = trait_resolver_data.generic_params.iter().collect();
let function_generic_params: Vec<GenericParamId<'_>> = resolver_data
.generic_params
.iter()
.filter(|param| !trait_params_set.contains(param))
.cloned()
.collect_vec();
Ok(DocumentableItemSignatureData {
item_id: DocumentableItemId::from(LookupItemId::TraitItem(Function(item_id))),
name: item_id.name(db),
visibility: Visibility::Private,
generic_args: None,
generic_params: Some(generic_params.to_vec()),
variants: None,
members: None,
return_type: Some(signature.return_type),
attributes: None,
params: Some(signature.params.clone()),
resolver_generic_params: Some(function_generic_params),
return_value_expr: None,
full_path: item_id.full_path(db),
})
}
fn get_impl_function_signature_data<'db>(
db: &'db dyn Database,
item_id: ImplFunctionId<'db>,
) -> Result<DocumentableItemSignatureData<'db>, SignatureError> {
let signature = db.impl_function_signature(item_id)?;
let generic_params = db.impl_function_generic_params(item_id)?;
Ok(DocumentableItemSignatureData {
item_id: DocumentableItemId::from(LookupItemId::ImplItem(ImplItemId::Function(item_id))),
name: item_id.name(db),
visibility: Visibility::Private,
generic_args: None,
generic_params: Some(generic_params.to_vec()),
variants: None,
members: None,
return_type: Some(signature.return_type),
attributes: None,
params: Some(signature.params.clone()),
resolver_generic_params: None,
return_value_expr: None,
full_path: item_id.full_path(db),
})
}
fn get_constant_signature_data<'db>(
db: &'db dyn Database,
item_id: ConstantId<'db>,
) -> Result<DocumentableItemSignatureData<'db>, SignatureError> {
let module_item_id = ModuleItemId::Constant(item_id);
let module_item_info = get_module_item_info(db, module_item_id)?;
let constant = db.constant_semantic_data(item_id)?;
Ok(DocumentableItemSignatureData {
item_id: DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::Constant(
item_id,
))),
name: item_id.name(db),
visibility: module_item_info.visibility,
generic_args: None,
generic_params: None,
variants: None,
members: None,
return_type: Some(constant.ty()),
attributes: None,
params: None,
resolver_generic_params: None,
return_value_expr: Some(constant.arenas.exprs[constant.value].clone()),
full_path: item_id.full_path(db),
})
}
fn get_impl_constant_signature_data<'db>(
db: &'db dyn Database,
item_id: ImplConstantDefId<'db>,
) -> Result<DocumentableItemSignatureData<'db>, SignatureError> {
let return_type = db.impl_constant_def_value(item_id)?.ty(db)?;
Ok(DocumentableItemSignatureData {
item_id: DocumentableItemId::from(LookupItemId::ImplItem(ImplItemId::Constant(item_id))),
name: item_id.name(db),
visibility: Visibility::Private,
generic_args: None,
generic_params: None,
variants: None,
members: None,
return_type: Some(return_type),
attributes: None,
params: None,
resolver_generic_params: None,
return_value_expr: None,
full_path: item_id.full_path(db),
})
}
fn get_trait_signature_data<'db>(
db: &'db dyn Database,
item_id: TraitId<'db>,
) -> Result<DocumentableItemSignatureData<'db>, SignatureError> {
let module_item_id = ModuleItemId::Trait(item_id);
let module_item_info = get_module_item_info(db, module_item_id)?;
let generic_params = db.trait_generic_params(item_id)?;
Ok(DocumentableItemSignatureData {
item_id: DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::Trait(item_id))),
name: item_id.name(db),
visibility: module_item_info.visibility,
generic_args: None,
generic_params: Some(generic_params.to_vec()),
variants: None,
members: None,
return_type: None,
attributes: None,
params: None,
resolver_generic_params: None,
return_value_expr: None,
full_path: item_id.full_path(db),
})
}
fn get_trait_const_signature_data<'db>(
db: &'db dyn Database,
item_id: TraitConstantId<'db>,
) -> Result<DocumentableItemSignatureData<'db>, SignatureError> {
let attributes = db.trait_constant_attributes(item_id)?;
let return_type = db.trait_constant_type(item_id)?;
Ok(DocumentableItemSignatureData {
item_id: DocumentableItemId::from(LookupItemId::TraitItem(TraitItemId::Constant(item_id))),
name: item_id.name(db),
visibility: Visibility::Private,
generic_args: None,
generic_params: None,
variants: None,
members: None,
return_type: Some(return_type),
attributes: Some(attributes),
params: None,
resolver_generic_params: None,
return_value_expr: None,
full_path: item_id.full_path(db),
})
}
fn get_impl_def_signature_data<'db>(
db: &'db dyn Database,
item_id: ImplDefId<'db>,
) -> Result<DocumentableItemSignatureData<'db>, SignatureError> {
let module_item_id = ModuleItemId::Impl(item_id);
let module_item_info = get_module_item_info(db, module_item_id)?;
let resolver_data = db.impl_def_resolver_data(item_id)?;
let intern = db.impl_def_concrete_trait(item_id)?.long(db);
let generic_params = db.impl_def_generic_params(item_id)?;
Ok(DocumentableItemSignatureData {
item_id: DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::Impl(item_id))),
name: item_id.name(db),
visibility: module_item_info.visibility,
generic_args: Some(intern.generic_args.clone()),
generic_params: Some(generic_params.to_vec()),
variants: None,
members: None,
return_type: None,
attributes: None,
params: None,
resolver_generic_params: Some(resolver_data.generic_params.clone()),
return_value_expr: None,
full_path: item_id.full_path(db),
})
}
fn get_impl_alias_signature_data<'db>(
db: &'db dyn Database,
item_id: ImplAliasId<'db>,
) -> Result<DocumentableItemSignatureData<'db>, SignatureError> {
let module_item_id = ModuleItemId::ImplAlias(item_id);
let module_item_info = get_module_item_info(db, module_item_id)?;
let resolved_impl = db.impl_alias_resolved_impl(item_id)?;
let intern = resolved_impl.concrete_trait(db)?.long(db);
let generic_params = db.impl_alias_generic_params(item_id)?;
Ok(DocumentableItemSignatureData {
item_id: DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::ImplAlias(
item_id,
))),
name: item_id.name(db),
visibility: module_item_info.visibility,
generic_args: Some(intern.generic_args.clone()),
generic_params: Some(generic_params),
variants: None,
members: None,
return_type: None,
attributes: None,
params: None,
resolver_generic_params: None,
return_value_expr: None,
full_path: item_id.full_path(db),
})
}
fn get_impl_impl_def_signature_data<'db>(
db: &'db dyn Database,
item_id: ImplImplDefId<'db>,
) -> Result<DocumentableItemSignatureData<'db>, SignatureError> {
Ok(DocumentableItemSignatureData {
item_id: DocumentableItemId::from(LookupItemId::ImplItem(ImplItemId::Impl(item_id))),
name: item_id.name(db),
visibility: Visibility::Private,
generic_args: None,
generic_params: None,
variants: None,
members: None,
return_type: None,
attributes: None,
params: None,
resolver_generic_params: None,
return_value_expr: None,
full_path: item_id.full_path(db),
})
}
fn get_module_type_alias_full_signature<'db>(
db: &'db dyn Database,
item_id: ModuleTypeAliasId<'db>,
) -> Result<DocumentableItemSignatureData<'db>, SignatureError> {
let module_item_id = ModuleItemId::TypeAlias(item_id);
let module_item_info = get_module_item_info(db, module_item_id)?;
let generic_params = db.module_type_alias_generic_params(item_id)?;
let resolved_type = db.module_type_alias_resolved_type(item_id)?;
Ok(DocumentableItemSignatureData {
item_id: DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::TypeAlias(
item_id,
))),
name: item_id.name(db),
visibility: module_item_info.visibility,
generic_args: None,
generic_params: Some(generic_params),
variants: None,
members: None,
return_type: Some(resolved_type),
attributes: None,
params: None,
resolver_generic_params: None,
return_value_expr: None,
full_path: item_id.full_path(db),
})
}
fn get_trait_type_full_signature<'db>(
db: &'db dyn Database,
item_id: TraitTypeId<'db>,
) -> Result<DocumentableItemSignatureData<'db>, SignatureError> {
let generic_params = db.trait_type_generic_params(item_id)?;
Ok(DocumentableItemSignatureData {
item_id: DocumentableItemId::from(LookupItemId::TraitItem(TraitItemId::Type(item_id))),
name: item_id.name(db),
visibility: Visibility::Private,
generic_args: None,
generic_params: Some(generic_params.to_vec()),
variants: None,
members: None,
return_type: None,
attributes: None,
params: None,
resolver_generic_params: None,
return_value_expr: None,
full_path: item_id.full_path(db),
})
}
fn get_impl_type_def_full_signature<'db>(
db: &'db dyn Database,
item_id: ImplTypeDefId<'db>,
) -> Result<DocumentableItemSignatureData<'db>, SignatureError> {
let resolved_type = db.impl_type_def_resolved_type(item_id)?;
let generic_params = db.impl_type_def_generic_params(item_id)?;
Ok(DocumentableItemSignatureData {
item_id: DocumentableItemId::from(LookupItemId::ImplItem(ImplItemId::Type(item_id))),
name: item_id.name(db),
visibility: Visibility::Private,
generic_args: None,
generic_params: Some(generic_params),
variants: None,
members: None,
return_type: Some(resolved_type),
attributes: None,
params: None,
resolver_generic_params: None,
return_value_expr: None,
full_path: item_id.full_path(db),
})
}
fn get_extern_type_full_signature<'db>(
db: &'db dyn Database,
item_id: ExternTypeId<'db>,
) -> Result<DocumentableItemSignatureData<'db>, SignatureError> {
let module_item_id = ModuleItemId::ExternType(item_id);
let module_item_info = get_module_item_info(db, module_item_id)?;
let generic_params = db.extern_type_declaration_generic_params(item_id)?;
Ok(DocumentableItemSignatureData {
item_id: DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::ExternType(
item_id,
))),
name: item_id.name(db),
visibility: module_item_info.visibility,
generic_args: None,
generic_params: Some(generic_params.to_vec()),
variants: None,
members: None,
return_type: None,
attributes: None,
params: None,
resolver_generic_params: None,
return_value_expr: None,
full_path: item_id.full_path(db),
})
}
fn get_extern_function_full_signature<'db>(
db: &'db dyn Database,
item_id: ExternFunctionId<'db>,
) -> Result<DocumentableItemSignatureData<'db>, SignatureError> {
let module_item_id = ModuleItemId::ExternFunction(item_id);
let module_item_info = get_module_item_info(db, module_item_id)?;
let generic_params = db.extern_function_declaration_generic_params(item_id)?;
let signature = db.extern_function_signature(item_id)?;
Ok(DocumentableItemSignatureData {
item_id: DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::ExternFunction(
item_id,
))),
name: item_id.name(db),
visibility: module_item_info.visibility,
generic_args: None,
generic_params: Some(generic_params.to_vec()),
variants: None,
members: None,
return_type: Some(signature.return_type),
attributes: None,
params: Some(signature.params.clone()),
resolver_generic_params: None,
return_value_expr: None,
full_path: item_id.full_path(db),
})
}