use super::*;
pub(crate) fn resolve_typedef(cursor: &Type, parser: &mut Parser<'_>) -> metadata::Type {
let decl = cursor.ty();
let name = decl.name();
if parser.header_root.is_some() {
if let Some(normalized) = normalize_string_alias(parser.namespace, &name) {
return normalized;
}
return flat_canonical(parser.namespace, &name, cursor, parser)
.unwrap_or_else(|| metadata::Type::value_named(parser.namespace, &name));
}
if let Some(ns) = parser.ref_map.get(&name) {
metadata::Type::value_named(ns, &name)
} else if let Some(ty) = universal_alias(parser.namespace, &name) {
ty
} else if let Some(scalar) = collapse_scalar_typedef(&name, cursor) {
scalar
} else if decl.is_from_main_file() {
metadata::Type::value_named(parser.namespace, &name)
} else {
parser.pending_typedefs.push(decl);
metadata::Type::value_named(parser.namespace, &name)
}
}
fn flat_canonical(
namespace: &str,
name: &str,
cursor: &Type,
parser: &mut Parser<'_>,
) -> Option<metadata::Type> {
if let Some(scalar) = semantic_scalar(name) {
return Some(scalar);
}
if let Some(scalar) = fundamental_scalar(name) {
return Some(scalar);
}
if let Some(scalar) = floating_typedef(cursor) {
return Some(scalar);
}
if let Some(scalar) = pointer_sized_abi(name) {
return Some(scalar);
}
if let Some(ty) = universal_alias(namespace, name) {
return Some(ty);
}
if let Some(leaf) = numerics_alias(name) {
return Some(metadata::Type::value_named(NUMERICS_NAMESPACE, leaf));
}
if let Some(base) = d2d_compat_alias(name) {
return Some(match numerics_alias(base) {
Some(leaf) => metadata::Type::value_named(NUMERICS_NAMESPACE, leaf),
None => metadata::Type::value_named(namespace, base),
});
}
interface_alias(cursor, parser)
}
fn universal_alias(namespace: &str, name: &str) -> Option<metadata::Type> {
if guid_alias(name) {
return Some(metadata::Type::value_named(namespace, "GUID"));
}
void_pointer_alias(name)
}
fn interface_alias(cursor: &Type, parser: &mut Parser<'_>) -> Option<metadata::Type> {
let underlying = cursor.ty().typedef_underlying_type();
if !is_interface_alias(&underlying) {
return None;
}
Some(if underlying.is_interface() {
underlying.to_type(parser)
} else {
underlying.pointee_type().to_type(parser)
})
}
pub(crate) fn is_interface_alias(underlying: &Type) -> bool {
underlying.is_interface()
|| (underlying.kind() == CXType_Pointer && underlying.pointee_type().is_interface())
}
fn collapse_scalar_typedef(name: &str, ty: &Type) -> Option<metadata::Type> {
if let Some(scalar) = pointer_sized_abi(name) {
return Some(scalar);
}
let canonical = ty.canonical_type();
is_fundamental_scalar_kind(canonical.kind()).then(|| scalar_kind_to_type(canonical.kind()))
}
pub(crate) fn floating_typedef(ty: &Type) -> Option<metadata::Type> {
match ty.canonical_type().kind() {
CXType_Float => Some(metadata::Type::F32),
CXType_Double | CXType_LongDouble => Some(metadata::Type::F64),
_ => None,
}
}
pub(crate) fn is_fundamental_scalar_kind(kind: CXTypeKind) -> bool {
matches!(
kind,
CXType_Bool
| CXType_Char_U
| CXType_UChar
| CXType_UShort
| CXType_UInt
| CXType_ULong
| CXType_ULongLong
| CXType_Char_S
| CXType_SChar
| CXType_Short
| CXType_Int
| CXType_Long
| CXType_LongLong
| CXType_Float
| CXType_Double
| CXType_WChar
| CXType_Char16
| CXType_Char32
)
}
pub(crate) fn fundamental_scalar(name: &str) -> Option<metadata::Type> {
Some(match name {
"BYTE" | "UCHAR" | "UINT8" | "uint8_t" => metadata::Type::U8,
"WORD" | "USHORT" | "WCHAR" | "UINT16" | "uint16_t" => metadata::Type::U16,
"DWORD" | "UINT" | "ULONG" | "DWORD32" | "UINT32" | "ULONG32" | "uint32_t" => {
metadata::Type::U32
}
"QWORD" | "ULONGLONG" | "DWORD64" | "UINT64" | "ULONG64" | "uint64_t" => {
metadata::Type::U64
}
"CHAR" | "INT8" | "int8_t" => metadata::Type::I8,
"SHORT" | "INT16" | "int16_t" => metadata::Type::I16,
"INT" | "LONG" | "INT32" | "LONG32" | "int32_t" => metadata::Type::I32,
"LONGLONG" | "INT64" | "LONG64" | "int64_t" => metadata::Type::I64,
_ => return None,
})
}
pub(crate) fn semantic_scalar(name: &str) -> Option<metadata::Type> {
Some(match name {
"BOOLEAN" => metadata::Type::Bool,
"LARGE_INTEGER" => metadata::Type::I64,
"ULARGE_INTEGER" => metadata::Type::U64,
_ => return None,
})
}
pub(crate) fn guid_alias(name: &str) -> bool {
matches!(name, "IID" | "CLSID" | "FMTID" | "UUID")
}
pub(crate) fn void_pointer_alias(name: &str) -> Option<metadata::Type> {
Some(match name {
"PVOID" | "LPVOID" | "PVOID64" => metadata::Type::PtrMut(Box::new(metadata::Type::Void), 1),
"LPCVOID" | "PCVOID" | "LPCTVOID" => {
metadata::Type::PtrConst(Box::new(metadata::Type::Void), 1)
}
_ => return None,
})
}
pub(crate) fn d2d_compat_alias(name: &str) -> Option<&'static str> {
Some(match name {
"D2D1_COLOR_F" => "D2D_COLOR_F",
"D2D1_MATRIX_3X2_F" => "D2D_MATRIX_3X2_F",
"D2D1_MATRIX_4X3_F" => "D2D_MATRIX_4X3_F",
"D2D1_MATRIX_4X4_F" => "D2D_MATRIX_4X4_F",
"D2D1_MATRIX_5X4_F" => "D2D_MATRIX_5X4_F",
"D2D1_POINT_2F" => "D2D_POINT_2F",
"D2D1_POINT_2L" => "D2D_POINT_2L",
"D2D1_POINT_2U" => "D2D_POINT_2U",
"D2D1_RECT_F" => "D2D_RECT_F",
"D2D1_RECT_L" => "D2D_RECT_L",
"D2D1_RECT_U" => "D2D_RECT_U",
"D2D1_SIZE_F" => "D2D_SIZE_F",
"D2D1_SIZE_U" => "D2D_SIZE_U",
"D2D1_VECTOR_2F" => "D2D_VECTOR_2F",
"D2D1_VECTOR_3F" => "D2D_VECTOR_3F",
"D2D1_VECTOR_4F" => "D2D_VECTOR_4F",
_ => return None,
})
}
pub(crate) fn numerics_alias(name: &str) -> Option<&'static str> {
Some(match name {
"D2D_MATRIX_3X2_F" => "Matrix3x2",
"D3DMATRIX" | "D2D_MATRIX_4X4_F" => "Matrix4x4",
"D2D_POINT_2F" | "D2D_VECTOR_2F" => "Vector2",
"D2D_VECTOR_3F" => "Vector3",
"D2D_VECTOR_4F" => "Vector4",
_ => return None,
})
}
pub(crate) const NUMERICS_NAMESPACE: &str = "Windows.Foundation.Numerics";
pub(crate) fn pointer_sized_abi(name: &str) -> Option<metadata::Type> {
match name {
"UINT_PTR" | "ULONG_PTR" | "DWORD_PTR" | "SIZE_T" | "size_t" | "rsize_t" | "uintptr_t" => {
Some(metadata::Type::USize)
}
"INT_PTR" | "LONG_PTR" | "SSIZE_T" | "intptr_t" | "ptrdiff_t" => {
Some(metadata::Type::ISize)
}
_ => None,
}
}
#[derive(Clone, Copy)]
enum AliasPolicy {
String {
canonical: &'static str,
mut_name: &'static str,
const_name: &'static str,
},
KeepNamed,
}
fn alias_policy(name: &str) -> Option<AliasPolicy> {
const WIDE: (&str, &str) = ("PWSTR", "PCWSTR");
const NARROW: (&str, &str) = ("PSTR", "PCSTR");
Some(match name {
"LPWSTR" | "PWSTR" => AliasPolicy::String {
canonical: WIDE.0,
mut_name: WIDE.0,
const_name: WIDE.1,
},
"LPCWSTR" | "PCWSTR" => AliasPolicy::String {
canonical: WIDE.1,
mut_name: WIDE.0,
const_name: WIDE.1,
},
"LPSTR" | "PSTR" => AliasPolicy::String {
canonical: NARROW.0,
mut_name: NARROW.0,
const_name: NARROW.1,
},
"LPCSTR" | "PCSTR" => AliasPolicy::String {
canonical: NARROW.1,
mut_name: NARROW.0,
const_name: NARROW.1,
},
"LPOLESTR" | "POLESTR" => AliasPolicy::String {
canonical: WIDE.0,
mut_name: WIDE.0,
const_name: WIDE.1,
},
"LPCOLESTR" | "PCOLESTR" => AliasPolicy::String {
canonical: WIDE.1,
mut_name: WIDE.0,
const_name: WIDE.1,
},
"BSTR" => AliasPolicy::KeepNamed,
_ => return None,
})
}
pub(crate) fn string_alias_canonical(name: &str) -> Option<&'static str> {
match alias_policy(name) {
Some(AliasPolicy::String { canonical, .. }) => Some(canonical),
_ => None,
}
}
fn normalize_string_alias(namespace: &str, name: &str) -> Option<metadata::Type> {
string_alias_canonical(name).map(|canonical| metadata::Type::value_named(namespace, canonical))
}
fn decay_array_param(
cursor_ty: &Type,
base: metadata::Type,
parser: &mut Parser<'_>,
) -> metadata::Type {
let canonical = cursor_ty.canonical_type();
if !matches!(
canonical.kind(),
CXType_ConstantArray | CXType_IncompleteArray
) {
return base;
}
let element = match base {
metadata::Type::ArrayFixed(element, _size) => *element,
_ => canonical.array_element_type().to_type(parser),
};
let is_const = canonical.array_element_type().is_const();
if is_const {
match element {
metadata::Type::PtrConst(t, n) => metadata::Type::PtrConst(t, n + 1),
other => metadata::Type::PtrConst(Box::new(other), 1),
}
} else {
match element {
metadata::Type::PtrMut(t, n) => metadata::Type::PtrMut(t, n + 1),
other => metadata::Type::PtrMut(Box::new(other), 1),
}
}
}
pub(crate) fn param_metadata_type(
cursor_ty: &Type,
annotation: &ParamAnnotation,
parser: &mut Parser<'_>,
) -> metadata::Type {
let base = cursor_ty.to_type(parser);
let base = decay_array_param(cursor_ty, base, parser);
let base = collapse_pointer_alias_param(cursor_ty, base, parser);
let ty = apply_sal_constness(base, annotation);
let ty = normalize_pointer_const_chain(ty);
let ty = promote_null_terminated_string(ty, annotation, parser);
requalify_string_alias(ty, parser)
}
pub(crate) fn inline_array_param_count(cursor_ty: &Type) -> Option<i32> {
if cursor_ty.kind() != CXType_ConstantArray {
return None;
}
let size = cursor_ty.array_size();
(size > 0).then_some(size as i32)
}
fn normalize_pointer_const_chain(ty: metadata::Type) -> metadata::Type {
fn flatten(inner: metadata::Type, depth: usize) -> (metadata::Type, usize) {
match inner {
metadata::Type::PtrMut(deeper, n) | metadata::Type::PtrConst(deeper, n) => {
flatten(*deeper, depth + n)
}
leaf => (leaf, depth),
}
}
match ty {
metadata::Type::PtrMut(inner, n)
if matches!(
*inner,
metadata::Type::PtrMut(..) | metadata::Type::PtrConst(..)
) =>
{
let (leaf, depth) = flatten(*inner, n);
metadata::Type::PtrMut(Box::new(leaf), depth)
}
metadata::Type::PtrConst(inner, n)
if matches!(
*inner,
metadata::Type::PtrMut(..) | metadata::Type::PtrConst(..)
) =>
{
let (leaf, depth) = flatten(*inner, n);
metadata::Type::PtrConst(Box::new(leaf), depth)
}
other => other,
}
}
pub(crate) fn normalize_rdl_type(ty: &metadata::Type) -> metadata::Type {
fn pointer_run(inner: &metadata::Type, depth: usize, is_const: bool) -> metadata::Type {
match inner {
metadata::Type::PtrMut(deeper, pointers) => {
pointer_run(deeper, depth + pointers, false)
}
metadata::Type::PtrConst(deeper, pointers) => {
pointer_run(deeper, depth + pointers, true)
}
leaf => {
let leaf = normalize_rdl_type(leaf);
if is_const {
metadata::Type::PtrConst(Box::new(leaf), depth)
} else {
metadata::Type::PtrMut(Box::new(leaf), depth)
}
}
}
}
match ty {
metadata::Type::Array(inner) => metadata::Type::Array(Box::new(normalize_rdl_type(inner))),
metadata::Type::ArrayFixed(inner, len) => {
metadata::Type::ArrayFixed(Box::new(normalize_rdl_type(inner)), *len)
}
metadata::Type::RefMut(inner) => {
metadata::Type::RefMut(Box::new(normalize_rdl_type(inner)))
}
metadata::Type::RefConst(inner) => {
metadata::Type::RefConst(Box::new(normalize_rdl_type(inner)))
}
metadata::Type::PtrMut(inner, pointers) => pointer_run(inner, *pointers, false),
metadata::Type::PtrConst(inner, pointers) => pointer_run(inner, *pointers, true),
metadata::Type::ClassName(type_name) => {
let mut type_name = type_name.clone();
type_name.generics = type_name.generics.iter().map(normalize_rdl_type).collect();
metadata::Type::ClassName(type_name)
}
metadata::Type::ValueName(type_name) => {
let mut type_name = type_name.clone();
type_name.generics = type_name.generics.iter().map(normalize_rdl_type).collect();
metadata::Type::ValueName(type_name)
}
other => other.clone(),
}
}
fn collapse_pointer_alias_param(
cursor_ty: &Type,
base: metadata::Type,
parser: &mut Parser<'_>,
) -> metadata::Type {
let metadata::Type::ValueName(ref type_name) = base else {
return base;
};
match alias_policy(&type_name.name) {
Some(AliasPolicy::String { canonical, .. }) => {
return metadata::Type::value_named(&type_name.namespace, canonical);
}
Some(AliasPolicy::KeepNamed) => return base,
None => {}
}
let mut underlying = cursor_ty.ty().typedef_underlying_type();
if underlying.kind() == CXType_Elaborated {
underlying = underlying.underlying_type();
}
if underlying.kind() != CXType_Pointer {
return base;
}
let pointee = underlying.pointee_type();
let pointee_canon = pointee.canonical_type();
match pointee_canon.kind() {
CXType_Void => base,
CXType_Record if pointee_canon.ty().name().ends_with("__") => base,
CXType_Record
if is_midl_placeholder_tag(&pointee_canon.ty().name())
&& is_handle_shape(&pointee_canon.ty()) =>
{
base
}
CXType_FunctionProto | CXType_FunctionNoProto => base,
_ => underlying.to_type(parser),
}
}
fn apply_sal_constness(ty: metadata::Type, annotation: &ParamAnnotation) -> metadata::Type {
if !annotation.is_annotated() {
return ty;
}
let make_const = if annotation.out_param {
false
} else if annotation.in_param || annotation.reserved {
true
} else {
return ty;
};
match ty {
metadata::Type::PtrMut(inner, n) | metadata::Type::PtrConst(inner, n) => {
if make_const {
metadata::Type::PtrConst(inner, n)
} else {
metadata::Type::PtrMut(inner, n)
}
}
metadata::Type::ValueName(ref type_name) => {
if let Some(AliasPolicy::String {
mut_name,
const_name,
..
}) = alias_policy(&type_name.name)
{
let variant = if make_const { const_name } else { mut_name };
metadata::Type::value_named(&type_name.namespace, variant)
} else {
ty
}
}
other => other,
}
}
fn promote_null_terminated_string(
ty: metadata::Type,
annotation: &ParamAnnotation,
parser: &Parser<'_>,
) -> metadata::Type {
if !annotation.null_terminated
|| parser.header_root.is_none()
|| annotation.size.is_some()
|| annotation.array.is_some()
{
return ty;
}
let (pointee, is_const) = match &ty {
metadata::Type::PtrConst(inner, 1) => (inner.as_ref(), true),
metadata::Type::PtrMut(inner, 1) => (inner.as_ref(), false),
_ => return ty,
};
let canonical = match (pointee, is_const) {
(metadata::Type::U16, true) => "PCWSTR",
(metadata::Type::U16, false) => "PWSTR",
(metadata::Type::I8 | metadata::Type::U8, true) => "PCSTR",
(metadata::Type::I8 | metadata::Type::U8, false) => "PSTR",
_ => return ty,
};
metadata::Type::value_named(parser.namespace, canonical)
}
fn requalify_string_alias(ty: metadata::Type, parser: &Parser<'_>) -> metadata::Type {
if let metadata::Type::ValueName(ref type_name) = ty
&& string_alias_canonical(&type_name.name).is_some()
&& let Some(ns) = parser.ref_map.get(&type_name.name)
{
return metadata::Type::value_named(ns, &type_name.name);
}
ty
}
pub(crate) fn scalar_kind_to_type(kind: CXTypeKind) -> metadata::Type {
match kind {
CXType_Bool => metadata::Type::Bool,
CXType_Char_U | CXType_UChar => metadata::Type::U8,
CXType_UShort | CXType_WChar | CXType_Char16 => metadata::Type::U16,
CXType_UInt | CXType_ULong | CXType_Char32 => metadata::Type::U32,
CXType_ULongLong => metadata::Type::U64,
CXType_Char_S | CXType_SChar => metadata::Type::I8,
CXType_Short => metadata::Type::I16,
CXType_Int | CXType_Long => metadata::Type::I32,
CXType_LongLong => metadata::Type::I64,
CXType_Float => metadata::Type::F32,
CXType_Double => metadata::Type::F64,
rest => panic!("{rest:?}"),
}
}