use clang::{Entity, EntityKind, Type, TypeKind};
use codehelion_helper::ir::TypeCategory;
pub(crate) fn category(ty: Type<'_>) -> TypeCategory {
let canonical = ty.get_canonical_type();
if let Some(declaration) = canonical.get_declaration()
&& is_parameter(declaration)
{
return TypeCategory::Parameter;
}
match canonical.get_kind() {
TypeKind::Void => TypeCategory::Nothing,
TypeKind::Bool => TypeCategory::Boolean,
TypeKind::CharS
| TypeKind::CharU
| TypeKind::SChar
| TypeKind::UChar
| TypeKind::WChar
| TypeKind::Char16
| TypeKind::Char32 => TypeCategory::Character,
TypeKind::Short
| TypeKind::UShort
| TypeKind::Int
| TypeKind::UInt
| TypeKind::Long
| TypeKind::ULong
| TypeKind::LongLong
| TypeKind::ULongLong
| TypeKind::Int128
| TypeKind::UInt128 => TypeCategory::Integer,
TypeKind::Half
| TypeKind::Float
| TypeKind::Double
| TypeKind::LongDouble
| TypeKind::Float128
| TypeKind::Complex => TypeCategory::Float,
TypeKind::Pointer
| TypeKind::BlockPointer
| TypeKind::MemberPointer
| TypeKind::LValueReference
| TypeKind::RValueReference
| TypeKind::ObjCObjectPointer
| TypeKind::Nullptr => TypeCategory::Handle,
TypeKind::ConstantArray
| TypeKind::IncompleteArray
| TypeKind::VariableArray
| TypeKind::DependentSizedArray
| TypeKind::Vector => TypeCategory::Sequence,
TypeKind::FunctionPrototype | TypeKind::FunctionNoPrototype => TypeCategory::Callable,
TypeKind::Enum => TypeCategory::Enumeration,
TypeKind::Record => record(canonical),
_ => TypeCategory::Unresolved,
}
}
fn record(canonical: Type<'_>) -> TypeCategory {
canonical
.get_declaration()
.and_then(standard_shape)
.unwrap_or(TypeCategory::Record)
}
fn standard_shape(declaration: Entity<'_>) -> Option<TypeCategory> {
if !in_standard_namespace(declaration) {
return None;
}
Some(match declaration.get_name()?.as_str() {
"basic_string" | "basic_string_view" | "string" | "string_view" | "filesystem" => {
TypeCategory::Text
}
"vector" | "array" | "deque" | "list" | "forward_list" | "valarray" | "span"
| "initializer_list" | "queue" | "stack" | "priority_queue" => TypeCategory::Sequence,
"map" | "multimap" | "set" | "multiset" | "unordered_map" | "unordered_multimap"
| "unordered_set" | "unordered_multiset" => TypeCategory::Mapping,
"pair" | "tuple" => TypeCategory::Tuple,
"unique_ptr" | "shared_ptr" | "weak_ptr" | "reference_wrapper" => TypeCategory::Handle,
"function" => TypeCategory::Callable,
"optional" | "expected" => TypeCategory::Enumeration,
_ => return None,
})
}
pub(crate) fn in_standard_namespace(entity: Entity<'_>) -> bool {
let mut parent = entity.get_semantic_parent();
while let Some(current) = parent {
if current.get_kind() == EntityKind::Namespace
&& current.get_name().as_deref() == Some("std")
{
return true;
}
parent = current.get_semantic_parent();
}
false
}
fn is_parameter(declaration: Entity<'_>) -> bool {
matches!(
declaration.get_kind(),
EntityKind::TemplateTypeParameter | EntityKind::NonTypeTemplateParameter
)
}