1use std::fmt;
7
8use crate::ast::BinOp;
9use crate::intern::InternedStr;
10
11#[derive(Debug, Clone)]
17pub enum TypeRepr {
18 CType {
20 specs: CTypeSpecs,
22 derived: Vec<CDerivedType>,
24 source: CTypeSource,
26 },
27
28 RustType {
30 repr: RustTypeRepr,
32 source: RustTypeSource,
34 },
35
36 Inferred(InferredType),
38}
39
40#[derive(Debug, Clone)]
46pub enum CTypeSource {
47 Header,
49 Apidoc { raw: String },
51 InlineFn { func_name: InternedStr },
53 Parser,
55 FieldInference { field_name: InternedStr },
57 Cast,
59 SvFamilyCast,
61 CommonMacroFieldInference,
66}
67
68#[derive(Debug, Clone)]
74pub enum RustTypeSource {
75 FnParam { func_name: String, param_index: usize },
77 FnReturn { func_name: String },
79 Const { const_name: String },
81 Parsed { raw: String },
83}
84
85#[derive(Debug, Clone, PartialEq, Eq)]
91pub enum CTypeSpecs {
92 Void,
94 Char { signed: Option<bool> },
96 Int { signed: bool, size: IntSize },
98 Float,
100 Double { is_long: bool },
102 Bool,
104 Struct { name: Option<InternedStr>, is_union: bool },
106 Enum { name: Option<InternedStr> },
108 TypedefName(InternedStr),
110 UnknownTypedef(String),
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116pub enum IntSize {
117 Short,
119 Int,
121 Long,
123 LongLong,
125 Int128,
127}
128
129#[derive(Debug, Clone, PartialEq, Eq)]
131pub enum CDerivedType {
132 Pointer {
134 is_const: bool,
135 is_volatile: bool,
136 is_restrict: bool,
137 },
138 Array { size: Option<usize> },
140 Function {
142 params: Vec<CTypeSpecs>,
143 variadic: bool,
144 },
145}
146
147#[derive(Debug, Clone, PartialEq, Eq)]
153pub enum RustTypeRepr {
154 CPrimitive(CPrimitiveKind),
156 RustPrimitive(RustPrimitiveKind),
158 Pointer {
160 inner: Box<RustTypeRepr>,
161 is_const: bool,
162 },
163 Reference {
165 inner: Box<RustTypeRepr>,
166 is_mut: bool,
167 },
168 Named(String),
170 Option(Box<RustTypeRepr>),
172 FnPointer {
174 params: Vec<RustTypeRepr>,
175 ret: Option<Box<RustTypeRepr>>,
176 },
177 Unit,
179 Unknown(String),
181}
182
183#[derive(Debug, Clone, Copy, PartialEq, Eq)]
185pub enum CPrimitiveKind {
186 CChar,
187 CSchar,
188 CUchar,
189 CShort,
190 CUshort,
191 CInt,
192 CUint,
193 CLong,
194 CUlong,
195 CLongLong,
196 CUlongLong,
197 CFloat,
198 CDouble,
199}
200
201#[derive(Debug, Clone, Copy, PartialEq, Eq)]
203pub enum RustPrimitiveKind {
204 I8,
205 I16,
206 I32,
207 I64,
208 I128,
209 Isize,
210 U8,
211 U16,
212 U32,
213 U64,
214 U128,
215 Usize,
216 F32,
217 F64,
218 Bool,
219}
220
221#[derive(Debug, Clone)]
227pub enum InferredType {
228 IntLiteral,
231 UIntLiteral,
233 FloatLiteral,
235 CharLiteral,
237 StringLiteral,
239
240 SymbolLookup {
243 name: InternedStr,
244 resolved_type: Box<TypeRepr>,
246 },
247 ThxDefault,
249
250 BinaryOp {
253 op: BinOp,
254 result_type: Box<TypeRepr>,
256 },
257 UnaryArithmetic {
259 inner_type: Box<TypeRepr>,
261 },
262 LogicalNot,
264 AddressOf { inner_type: Box<TypeRepr> },
266 Dereference { pointer_type: Box<TypeRepr> },
268 IncDec { inner_type: Box<TypeRepr> },
270
271 MemberAccess {
274 base_type: String,
275 member: InternedStr,
276 field_type: Option<Box<TypeRepr>>,
278 },
279 PtrMemberAccess {
281 base_type: String,
282 member: InternedStr,
283 field_type: Option<Box<TypeRepr>>,
285 used_consistent_type: bool,
287 },
288
289 ArraySubscript {
292 base_type: Box<TypeRepr>,
293 element_type: Box<TypeRepr>,
295 },
296
297 Conditional {
300 then_type: Box<TypeRepr>,
301 else_type: Box<TypeRepr>,
302 result_type: Box<TypeRepr>,
304 },
305 Comma {
307 rhs_type: Box<TypeRepr>,
309 },
310 Assignment {
312 lhs_type: Box<TypeRepr>,
314 },
315
316 Cast { target_type: Box<TypeRepr> },
319 Sizeof,
321 Alignof,
323 CompoundLiteral { type_name: Box<TypeRepr> },
325
326 StmtExpr {
329 last_expr_type: Option<Box<TypeRepr>>,
331 },
332 Assert,
334 FunctionReturn { func_name: InternedStr },
336}
337
338impl CTypeSpecs {
343 pub fn from_decl_specs(specs: &crate::ast::DeclSpecs, _interner: &crate::intern::StringInterner) -> Self {
345 use crate::ast::TypeSpec;
346
347 let mut has_signed = false;
348 let mut has_unsigned = false;
349 let mut has_short = false;
350 let mut has_long: u8 = 0;
351 let mut base_type: Option<CTypeSpecs> = None;
352
353 for type_spec in &specs.type_specs {
354 match type_spec {
355 TypeSpec::Void => base_type = Some(CTypeSpecs::Void),
356 TypeSpec::Char => {
357 if base_type.is_none() {
359 base_type = Some(CTypeSpecs::Char { signed: None });
360 }
361 }
362 TypeSpec::Short => has_short = true,
363 TypeSpec::Int => {
364 if base_type.is_none() {
365 base_type = Some(CTypeSpecs::Int {
366 signed: true,
367 size: IntSize::Int,
368 });
369 }
370 }
371 TypeSpec::Long => has_long += 1,
372 TypeSpec::Float => base_type = Some(CTypeSpecs::Float),
373 TypeSpec::Double => base_type = Some(CTypeSpecs::Double { is_long: false }),
374 TypeSpec::Signed => has_signed = true,
375 TypeSpec::Unsigned => has_unsigned = true,
376 TypeSpec::Bool => base_type = Some(CTypeSpecs::Bool),
377 TypeSpec::Int128 => {
378 base_type = Some(CTypeSpecs::Int {
379 signed: !has_unsigned,
380 size: IntSize::Int128,
381 });
382 }
383 TypeSpec::Struct(s) => {
384 base_type = Some(CTypeSpecs::Struct {
385 name: s.name,
386 is_union: false,
387 });
388 }
389 TypeSpec::Union(s) => {
390 base_type = Some(CTypeSpecs::Struct {
391 name: s.name,
392 is_union: true,
393 });
394 }
395 TypeSpec::Enum(e) => {
396 base_type = Some(CTypeSpecs::Enum { name: e.name });
397 }
398 TypeSpec::TypedefName(name) => {
399 base_type = Some(CTypeSpecs::TypedefName(*name));
400 }
401 _ => {}
402 }
403 }
404
405 if has_short {
407 return CTypeSpecs::Int {
408 signed: !has_unsigned,
409 size: IntSize::Short,
410 };
411 }
412
413 if has_long >= 2 {
414 return CTypeSpecs::Int {
415 signed: !has_unsigned,
416 size: IntSize::LongLong,
417 };
418 }
419
420 if has_long == 1 {
421 if let Some(CTypeSpecs::Double { .. }) = base_type {
422 return CTypeSpecs::Double { is_long: true };
423 }
424 return CTypeSpecs::Int {
425 signed: !has_unsigned,
426 size: IntSize::Long,
427 };
428 }
429
430 if let Some(CTypeSpecs::Char { .. }) = base_type {
432 if has_signed {
433 return CTypeSpecs::Char { signed: Some(true) };
434 } else if has_unsigned {
435 return CTypeSpecs::Char { signed: Some(false) };
436 }
437 return CTypeSpecs::Char { signed: None };
438 }
439
440 if has_unsigned && base_type.is_none() {
442 return CTypeSpecs::Int {
443 signed: false,
444 size: IntSize::Int,
445 };
446 }
447 if has_signed && base_type.is_none() {
448 return CTypeSpecs::Int {
449 signed: true,
450 size: IntSize::Int,
451 };
452 }
453
454 if has_unsigned {
456 if let Some(CTypeSpecs::Int { size, .. }) = base_type {
457 return CTypeSpecs::Int {
458 signed: false,
459 size,
460 };
461 }
462 }
463
464 base_type.unwrap_or(CTypeSpecs::Int {
465 signed: true,
466 size: IntSize::Int,
467 })
468 }
469}
470
471impl CDerivedType {
472 pub fn from_derived_decls(derived: &[crate::ast::DerivedDecl]) -> Vec<Self> {
474 use crate::ast::ExprKind;
475
476 derived
477 .iter()
478 .map(|d| match d {
479 crate::ast::DerivedDecl::Pointer(quals) => CDerivedType::Pointer {
480 is_const: quals.is_const,
481 is_volatile: quals.is_volatile,
482 is_restrict: quals.is_restrict,
483 },
484 crate::ast::DerivedDecl::Array(array_decl) => {
485 let size = array_decl.size.as_ref().and_then(|expr| {
487 match &expr.kind {
488 ExprKind::IntLit(n) => Some(*n as usize),
489 ExprKind::UIntLit(n) => Some(*n as usize),
490 _ => None,
491 }
492 });
493 CDerivedType::Array { size }
494 }
495 crate::ast::DerivedDecl::Function(_params) => {
496 CDerivedType::Function {
498 params: vec![],
499 variadic: false,
500 }
501 }
502 })
503 .collect()
504 }
505}
506
507impl RustTypeRepr {
508 pub fn from_type_string(s: &str) -> Self {
510 let s = s.trim();
511
512 if s == "()" {
514 return RustTypeRepr::Unit;
515 }
516
517 if let Some(rest) = s.strip_prefix("*mut ") {
519 return RustTypeRepr::Pointer {
520 inner: Box::new(Self::from_type_string(rest)),
521 is_const: false,
522 };
523 }
524 if let Some(rest) = s.strip_prefix("* mut ") {
525 return RustTypeRepr::Pointer {
526 inner: Box::new(Self::from_type_string(rest)),
527 is_const: false,
528 };
529 }
530 if let Some(rest) = s.strip_prefix("*const ") {
531 return RustTypeRepr::Pointer {
532 inner: Box::new(Self::from_type_string(rest)),
533 is_const: true,
534 };
535 }
536 if let Some(rest) = s.strip_prefix("* const ") {
537 return RustTypeRepr::Pointer {
538 inner: Box::new(Self::from_type_string(rest)),
539 is_const: true,
540 };
541 }
542
543 if let Some(rest) = s.strip_prefix("&mut ") {
545 return RustTypeRepr::Reference {
546 inner: Box::new(Self::from_type_string(rest)),
547 is_mut: true,
548 };
549 }
550 if let Some(rest) = s.strip_prefix("& mut ") {
551 return RustTypeRepr::Reference {
552 inner: Box::new(Self::from_type_string(rest)),
553 is_mut: true,
554 };
555 }
556 if let Some(rest) = s.strip_prefix('&') {
557 return RustTypeRepr::Reference {
558 inner: Box::new(Self::from_type_string(rest.trim())),
559 is_mut: false,
560 };
561 }
562
563 if let Some(kind) = Self::parse_c_primitive(s) {
565 return RustTypeRepr::CPrimitive(kind);
566 }
567
568 if let Some(kind) = Self::parse_rust_primitive(s) {
570 return RustTypeRepr::RustPrimitive(kind);
571 }
572
573 if s.starts_with("Option<") || s.starts_with(":: std :: option :: Option<") {
575 if let Some(inner) = Self::extract_generic_param(s, "Option") {
576 return RustTypeRepr::Option(Box::new(Self::from_type_string(&inner)));
577 }
578 }
579
580 if s.chars().next().map(|c| c.is_alphabetic() || c == '_').unwrap_or(false) {
582 let name = s.split("::").last().unwrap_or(s).trim();
584 return RustTypeRepr::Named(name.to_string());
585 }
586
587 RustTypeRepr::Unknown(s.to_string())
589 }
590
591 fn parse_c_primitive(s: &str) -> Option<CPrimitiveKind> {
593 let s = s.trim();
595 let name = if s.contains("::") {
596 s.split("::").last()?.trim()
597 } else {
598 s
599 };
600
601 match name {
602 "c_char" => Some(CPrimitiveKind::CChar),
603 "c_schar" => Some(CPrimitiveKind::CSchar),
604 "c_uchar" => Some(CPrimitiveKind::CUchar),
605 "c_short" => Some(CPrimitiveKind::CShort),
606 "c_ushort" => Some(CPrimitiveKind::CUshort),
607 "c_int" => Some(CPrimitiveKind::CInt),
608 "c_uint" => Some(CPrimitiveKind::CUint),
609 "c_long" => Some(CPrimitiveKind::CLong),
610 "c_ulong" => Some(CPrimitiveKind::CUlong),
611 "c_longlong" => Some(CPrimitiveKind::CLongLong),
612 "c_ulonglong" => Some(CPrimitiveKind::CUlongLong),
613 "c_float" => Some(CPrimitiveKind::CFloat),
614 "c_double" => Some(CPrimitiveKind::CDouble),
615 _ => None,
616 }
617 }
618
619 fn parse_rust_primitive(s: &str) -> Option<RustPrimitiveKind> {
621 match s.trim() {
622 "i8" => Some(RustPrimitiveKind::I8),
623 "i16" => Some(RustPrimitiveKind::I16),
624 "i32" => Some(RustPrimitiveKind::I32),
625 "i64" => Some(RustPrimitiveKind::I64),
626 "i128" => Some(RustPrimitiveKind::I128),
627 "isize" => Some(RustPrimitiveKind::Isize),
628 "u8" => Some(RustPrimitiveKind::U8),
629 "u16" => Some(RustPrimitiveKind::U16),
630 "u32" => Some(RustPrimitiveKind::U32),
631 "u64" => Some(RustPrimitiveKind::U64),
632 "u128" => Some(RustPrimitiveKind::U128),
633 "usize" => Some(RustPrimitiveKind::Usize),
634 "f32" => Some(RustPrimitiveKind::F32),
635 "f64" => Some(RustPrimitiveKind::F64),
636 "bool" => Some(RustPrimitiveKind::Bool),
637 _ => None,
638 }
639 }
640
641 fn extract_generic_param(s: &str, type_name: &str) -> Option<String> {
643 let start = s.find(&format!("{}<", type_name))?;
645 let after_open = start + type_name.len() + 1;
646 let content = &s[after_open..];
647
648 let mut depth = 1;
650 let mut end = 0;
651 for (i, c) in content.char_indices() {
652 match c {
653 '<' => depth += 1,
654 '>' => {
655 depth -= 1;
656 if depth == 0 {
657 end = i;
658 break;
659 }
660 }
661 _ => {}
662 }
663 }
664
665 if end > 0 {
666 Some(content[..end].trim().to_string())
667 } else {
668 None
669 }
670 }
671}
672
673impl TypeRepr {
674 pub fn source_display(&self) -> &'static str {
676 match self {
677 TypeRepr::CType { source, .. } => match source {
678 CTypeSource::Header => "c-header",
679 CTypeSource::Apidoc { .. } => "apidoc",
680 CTypeSource::InlineFn { .. } => "inline-fn",
681 CTypeSource::Parser => "parser",
682 CTypeSource::FieldInference { .. } => "field-inference",
683 CTypeSource::Cast => "cast",
684 CTypeSource::SvFamilyCast => "sv-family-cast",
685 CTypeSource::CommonMacroFieldInference => "common-macro-field-inference",
686 },
687 TypeRepr::RustType { .. } => "rust-bindings",
688 TypeRepr::Inferred(_) => "inferred",
689 }
690 }
691
692 pub fn is_fn_param_source(&self) -> bool {
698 matches!(self, TypeRepr::RustType { source: RustTypeSource::FnParam { .. }, .. })
699 }
700
701 pub fn confidence_tier(&self) -> u8 {
708 match self {
709 TypeRepr::RustType { source, .. } => match source {
710 RustTypeSource::FnParam { .. }
711 | RustTypeSource::FnReturn { .. }
712 | RustTypeSource::Const { .. } => 1,
713 RustTypeSource::Parsed { .. } => 3,
714 },
715 TypeRepr::CType { source, .. } => match source {
716 CTypeSource::InlineFn { .. } | CTypeSource::Header => 2,
717 CTypeSource::Apidoc { .. }
718 | CTypeSource::CommonMacroFieldInference => 3,
719 CTypeSource::Cast
720 | CTypeSource::SvFamilyCast
721 | CTypeSource::FieldInference { .. }
722 | CTypeSource::Parser => 4,
723 },
724 TypeRepr::Inferred(_) => 4,
725 }
726 }
727
728 pub fn is_void(&self) -> bool {
729 match self {
730 TypeRepr::CType { specs, derived, .. } => {
731 derived.is_empty() && matches!(specs, CTypeSpecs::Void)
733 }
734 TypeRepr::RustType { repr, .. } => {
735 matches!(repr, RustTypeRepr::Unit)
736 }
737 TypeRepr::Inferred(inferred) => {
738 match inferred {
739 InferredType::SymbolLookup { resolved_type, .. } => {
740 resolved_type.is_void()
741 }
742 _ => false,
743 }
744 }
745 }
746 }
747
748 pub fn make_outer_pointer_mut(&mut self) {
751 match self {
752 TypeRepr::CType { derived, .. } => {
753 for d in derived.iter_mut().rev() {
754 if let CDerivedType::Pointer { is_const, .. } = d {
755 *is_const = false;
756 return;
757 }
758 }
759 }
760 TypeRepr::RustType { repr, .. } => {
761 if let RustTypeRepr::Pointer { is_const, .. } = repr {
762 *is_const = false;
763 }
764 }
765 _ => {}
766 }
767 }
768
769 pub fn make_outer_pointer_const(&mut self) {
770 match self {
771 TypeRepr::CType { derived, .. } => {
772 for d in derived.iter_mut().rev() {
774 if let CDerivedType::Pointer { is_const, .. } = d {
775 *is_const = true;
776 return;
777 }
778 }
779 }
780 TypeRepr::RustType { repr, .. } => {
781 repr.make_outer_pointer_const();
782 }
783 TypeRepr::Inferred(inferred) => {
784 match inferred {
785 InferredType::SymbolLookup { resolved_type, .. } => {
786 resolved_type.make_outer_pointer_const();
787 }
788 InferredType::Cast { target_type } => {
789 target_type.make_outer_pointer_const();
790 }
791 _ => {}
792 }
793 }
794 }
795 }
796
797 pub fn is_pointer_type(&self) -> bool {
804 match self {
805 TypeRepr::CType { derived, .. } => {
806 derived.iter().any(|d| matches!(d, CDerivedType::Pointer { .. }))
807 }
808 TypeRepr::RustType { repr, .. } => repr.has_outer_pointer(),
809 TypeRepr::Inferred(inferred) => inferred
810 .resolved_type()
811 .is_some_and(|t| t.is_pointer_type()),
812 }
813 }
814
815 pub fn is_void_pointer(&self) -> bool {
821 match self {
822 TypeRepr::CType { specs, derived, .. } => {
823 derived.iter().any(|d| matches!(d, CDerivedType::Pointer { .. }))
824 && matches!(specs, CTypeSpecs::Void)
825 }
826 TypeRepr::RustType { repr, .. } => match repr {
827 RustTypeRepr::Pointer { inner, .. } => {
828 matches!(inner.as_ref(), RustTypeRepr::Unit)
829 || matches!(inner.as_ref(), RustTypeRepr::Named(n) if n == "c_void")
830 }
831 _ => false,
832 },
833 TypeRepr::Inferred(inferred) => inferred
834 .resolved_type()
835 .is_some_and(|t| t.is_void_pointer()),
836 }
837 }
838
839 pub fn is_concrete_pointer(&self) -> bool {
841 self.is_pointer_type() && !self.is_void_pointer()
842 }
843
844 pub fn has_outer_pointer(&self) -> bool {
849 match self {
850 TypeRepr::CType { derived, .. } => {
851 derived.iter().any(|d| matches!(d, CDerivedType::Pointer { .. }))
852 }
853 TypeRepr::RustType { repr, .. } => repr.has_outer_pointer(),
854 _ => false,
855 }
856 }
857
858 pub fn from_apidoc_string(s: &str, interner: &crate::intern::StringInterner) -> Self {
860 let (specs, derived) = Self::parse_c_type_string(s, interner);
862 TypeRepr::CType {
863 specs,
864 derived,
865 source: CTypeSource::Apidoc { raw: s.to_string() },
866 }
867 }
868
869 pub fn from_rust_string(s: &str) -> Self {
874 let repr = RustTypeRepr::from_type_string(s);
875 TypeRepr::RustType {
876 repr,
877 source: RustTypeSource::Parsed {
878 raw: s.to_string(),
879 },
880 }
881 }
882
883 pub fn from_unified_type(
896 ut: &crate::unified_type::UnifiedType,
897 interner: &crate::intern::StringInterner,
898 ) -> Self {
899 let raw = ut.to_rust_string();
900 if let Some((specs, derived)) = unified_to_c(ut, interner) {
901 return TypeRepr::CType {
902 specs,
903 derived,
904 source: CTypeSource::Apidoc { raw },
905 };
906 }
907 TypeRepr::RustType {
908 repr: RustTypeRepr::Unknown(raw.clone()),
909 source: RustTypeSource::Parsed { raw },
910 }
911 }
912
913 pub fn from_decl(
918 specs: &crate::ast::DeclSpecs,
919 declarator: &crate::ast::Declarator,
920 _interner: &crate::intern::StringInterner,
921 ) -> Self {
922 let c_specs = CTypeSpecs::from_decl_specs(specs, _interner);
923 let derived = CDerivedType::from_derived_decls(&declarator.derived);
924 TypeRepr::CType {
925 specs: c_specs,
926 derived,
927 source: CTypeSource::Header,
928 }
929 }
930
931 pub fn from_type_name(
936 type_name: &crate::ast::TypeName,
937 interner: &crate::intern::StringInterner,
938 ) -> Self {
939 let c_specs = CTypeSpecs::from_decl_specs(&type_name.specs, interner);
940 let derived = type_name.declarator
941 .as_ref()
942 .map(|d| CDerivedType::from_derived_decls(&d.derived))
943 .unwrap_or_default();
944 TypeRepr::CType {
945 specs: c_specs,
946 derived,
947 source: CTypeSource::Parser,
948 }
949 }
950
951 pub fn from_c_type_string(
959 s: &str,
960 interner: &crate::intern::StringInterner,
961 files: &crate::source::FileRegistry,
962 typedefs: &std::collections::HashSet<crate::intern::InternedStr>,
963 ) -> Self {
964 use crate::parser::parse_type_from_string;
965
966 match parse_type_from_string(s, interner, files, typedefs) {
967 Ok(type_name) => Self::from_type_name(&type_name, interner),
968 Err(_) => {
969 let (specs, derived) = Self::parse_c_type_string(s, interner);
971 TypeRepr::CType {
972 specs,
973 derived,
974 source: CTypeSource::Apidoc { raw: s.to_string() },
975 }
976 }
977 }
978 }
979
980 fn parse_c_type_string(s: &str, interner: &crate::intern::StringInterner) -> (CTypeSpecs, Vec<CDerivedType>) {
982 let s = s.trim();
983
984 let mut prefix_pointers: Vec<bool> = Vec::new(); let mut current = s;
990 loop {
991 if let Some(rest) = current.strip_prefix("*mut ") {
992 prefix_pointers.push(false);
993 current = rest.trim();
994 } else if let Some(rest) = current.strip_prefix("*const ") {
995 prefix_pointers.push(true);
996 current = rest.trim();
997 } else {
998 break;
999 }
1000 }
1001
1002 let mut ptr_count = 0;
1004 let mut is_const = false;
1005 let mut base = current;
1006
1007 while base.ends_with('*') {
1009 ptr_count += 1;
1010 base = base[..base.len() - 1].trim();
1011 }
1012
1013 if base.starts_with("const ") {
1015 is_const = true;
1016 base = base[6..].trim();
1017 }
1018 if base.ends_with(" const") {
1019 is_const = true;
1020 base = base[..base.len() - 6].trim();
1021 }
1022
1023 let specs = Self::parse_c_base_type(base, interner);
1025
1026 let mut derived: Vec<CDerivedType> = Vec::with_capacity(prefix_pointers.len() + ptr_count);
1031 for is_const_p in prefix_pointers.iter().rev() {
1032 derived.push(CDerivedType::Pointer {
1033 is_const: *is_const_p,
1034 is_volatile: false,
1035 is_restrict: false,
1036 });
1037 }
1038 for i in 0..ptr_count {
1039 derived.push(CDerivedType::Pointer {
1040 is_const: i == 0 && is_const,
1041 is_volatile: false,
1042 is_restrict: false,
1043 });
1044 }
1045
1046 (specs, derived)
1047 }
1048
1049 fn parse_c_base_type(s: &str, interner: &crate::intern::StringInterner) -> CTypeSpecs {
1051 match s {
1052 "void" => CTypeSpecs::Void,
1053 "char" => CTypeSpecs::Char { signed: None },
1054 "signed char" => CTypeSpecs::Char { signed: Some(true) },
1055 "unsigned char" => CTypeSpecs::Char { signed: Some(false) },
1056 "short" | "short int" | "signed short" | "signed short int" => {
1057 CTypeSpecs::Int { signed: true, size: IntSize::Short }
1058 }
1059 "unsigned short" | "unsigned short int" => {
1060 CTypeSpecs::Int { signed: false, size: IntSize::Short }
1061 }
1062 "int" | "signed" | "signed int" => {
1063 CTypeSpecs::Int { signed: true, size: IntSize::Int }
1064 }
1065 "unsigned" | "unsigned int" => {
1066 CTypeSpecs::Int { signed: false, size: IntSize::Int }
1067 }
1068 "long" | "long int" | "signed long" | "signed long int" => {
1069 CTypeSpecs::Int { signed: true, size: IntSize::Long }
1070 }
1071 "unsigned long" | "unsigned long int" => {
1072 CTypeSpecs::Int { signed: false, size: IntSize::Long }
1073 }
1074 "long long" | "long long int" | "signed long long" | "signed long long int" => {
1075 CTypeSpecs::Int { signed: true, size: IntSize::LongLong }
1076 }
1077 "unsigned long long" | "unsigned long long int" => {
1078 CTypeSpecs::Int { signed: false, size: IntSize::LongLong }
1079 }
1080 "float" => CTypeSpecs::Float,
1081 "double" => CTypeSpecs::Double { is_long: false },
1082 "long double" => CTypeSpecs::Double { is_long: true },
1083 "_Bool" | "bool" => CTypeSpecs::Bool,
1084 _ => {
1085 if let Some(rest) = s.strip_prefix("struct ") {
1087 if let Some(name) = interner.lookup(rest.trim()) {
1088 return CTypeSpecs::Struct { name: Some(name), is_union: false };
1089 }
1090 return CTypeSpecs::Struct { name: None, is_union: false };
1091 }
1092 if let Some(rest) = s.strip_prefix("union ") {
1093 if let Some(name) = interner.lookup(rest.trim()) {
1094 return CTypeSpecs::Struct { name: Some(name), is_union: true };
1095 }
1096 return CTypeSpecs::Struct { name: None, is_union: true };
1097 }
1098 if let Some(rest) = s.strip_prefix("enum ") {
1099 if let Some(name) = interner.lookup(rest.trim()) {
1100 return CTypeSpecs::Enum { name: Some(name) };
1101 }
1102 return CTypeSpecs::Enum { name: None };
1103 }
1104 if let Some(name) = interner.lookup(s) {
1106 CTypeSpecs::TypedefName(name)
1107 } else {
1108 CTypeSpecs::Void }
1112 }
1113 }
1114 }
1115
1116 pub fn to_display_string(&self, interner: &crate::intern::StringInterner) -> String {
1118 match self {
1119 TypeRepr::CType { specs, derived, .. } => {
1120 let base = specs.to_display_string(interner);
1121 let mut result = base;
1122 for d in derived {
1123 match d {
1124 CDerivedType::Pointer { is_const: true, .. } => result.push_str(" *const"),
1125 CDerivedType::Pointer { .. } => result.push_str(" *"),
1126 CDerivedType::Array { size: Some(n) } => {
1127 result.push_str(&format!("[{}]", n));
1128 }
1129 CDerivedType::Array { size: None } => result.push_str("[]"),
1130 CDerivedType::Function { .. } => result.push_str("()"),
1131 }
1132 }
1133 result
1134 }
1135 TypeRepr::RustType { repr, .. } => repr.to_display_string(),
1136 TypeRepr::Inferred(inferred) => inferred.to_display_string(interner),
1137 }
1138 }
1139
1140 pub fn to_rust_string(&self, interner: &crate::intern::StringInterner) -> String {
1142 match self {
1143 TypeRepr::CType { specs, derived, .. } => {
1144 let base = specs.to_rust_string(interner);
1145 let mut result = base;
1147 for d in derived.iter().rev() {
1148 if result == "()" && matches!(d, CDerivedType::Pointer { .. } | CDerivedType::Array { .. }) {
1150 result = "c_void".to_string();
1151 }
1152 result = match d {
1153 CDerivedType::Pointer { is_const: true, .. } => format!("*const {}", result),
1154 CDerivedType::Pointer { .. } => format!("*mut {}", result),
1155 CDerivedType::Array { size: Some(n) } => format!("[{}; {}]", result, n),
1156 CDerivedType::Array { size: None } => format!("*mut {}", result),
1157 CDerivedType::Function { .. } => format!("/* fn */"),
1158 };
1159 }
1160 result
1161 }
1162 TypeRepr::RustType { repr, .. } => repr.to_display_string(),
1163 TypeRepr::Inferred(inferred) => inferred.to_rust_string(interner),
1164 }
1165 }
1166}
1167
1168fn unified_to_c(
1175 ut: &crate::unified_type::UnifiedType,
1176 interner: &crate::intern::StringInterner,
1177) -> Option<(CTypeSpecs, Vec<CDerivedType>)> {
1178 use crate::unified_type::{UnifiedType as UT, IntSize as UIS};
1179
1180 match ut {
1181 UT::Void => Some((CTypeSpecs::Void, vec![])),
1182 UT::Bool => Some((CTypeSpecs::Bool, vec![])),
1183 UT::Char { signed } => Some((CTypeSpecs::Char { signed: *signed }, vec![])),
1184 UT::Int { signed, size } => {
1185 if matches!(size, UIS::Char) {
1188 return Some((CTypeSpecs::Char { signed: Some(*signed) }, vec![]));
1189 }
1190 let target = match size {
1191 UIS::Char => unreachable!(),
1192 UIS::Short => IntSize::Short,
1193 UIS::Int => IntSize::Int,
1194 UIS::Long => IntSize::Long,
1195 UIS::LongLong => IntSize::LongLong,
1196 UIS::Int128 => IntSize::Int128,
1197 };
1198 Some((CTypeSpecs::Int { signed: *signed, size: target }, vec![]))
1199 }
1200 UT::Float => Some((CTypeSpecs::Float, vec![])),
1201 UT::Double => Some((CTypeSpecs::Double { is_long: false }, vec![])),
1202 UT::LongDouble => Some((CTypeSpecs::Double { is_long: true }, vec![])),
1203 UT::Pointer { inner, is_const } => {
1204 let (specs, mut derived) = unified_to_c(inner, interner)?;
1205 derived.insert(
1207 0,
1208 CDerivedType::Pointer {
1209 is_const: *is_const,
1210 is_volatile: false,
1211 is_restrict: false,
1212 },
1213 );
1214 Some((specs, derived))
1215 }
1216 UT::Array { inner, size } => {
1217 let (specs, mut derived) = unified_to_c(inner, interner)?;
1218 derived.insert(0, CDerivedType::Array { size: *size });
1219 Some((specs, derived))
1220 }
1221 UT::Named(name) => {
1222 let specs = match interner.lookup(name) {
1223 Some(id) => CTypeSpecs::TypedefName(id),
1224 None => CTypeSpecs::UnknownTypedef(name.clone()),
1225 };
1226 Some((specs, vec![]))
1227 }
1228 UT::FnPtr { .. } | UT::Verbatim(_) | UT::Unknown => None,
1229 }
1230}
1231
1232impl TypeRepr {
1237 pub fn pointee_name(&self) -> Option<InternedStr> {
1242 match self {
1243 TypeRepr::CType { specs, derived, .. } => {
1244 if derived.iter().any(|d| matches!(d, CDerivedType::Pointer { .. })) {
1245 specs.type_name()
1246 } else {
1247 None
1248 }
1249 }
1250 TypeRepr::RustType { repr, .. } => repr.pointee_name(),
1251 TypeRepr::Inferred(inferred) => inferred.resolved_type()?.pointee_name(),
1252 }
1253 }
1254
1255 pub fn type_name(&self) -> Option<InternedStr> {
1260 match self {
1261 TypeRepr::CType { specs, .. } => specs.type_name(),
1262 TypeRepr::RustType { repr, .. } => repr.type_name(),
1263 TypeRepr::Inferred(inferred) => inferred.resolved_type()?.type_name(),
1264 }
1265 }
1266}
1267
1268impl CTypeSpecs {
1269 pub fn type_name(&self) -> Option<InternedStr> {
1271 match self {
1272 CTypeSpecs::Struct { name: Some(n), .. } => Some(*n),
1273 CTypeSpecs::TypedefName(n) => Some(*n),
1274 CTypeSpecs::Enum { name: Some(n) } => Some(*n),
1275 _ => None,
1276 }
1277 }
1278}
1279
1280impl InferredType {
1281 pub fn resolved_type(&self) -> Option<&TypeRepr> {
1286 match self {
1287 InferredType::Cast { target_type } => Some(target_type),
1288 InferredType::PtrMemberAccess { field_type: Some(ft), .. } => Some(ft),
1289 InferredType::MemberAccess { field_type: Some(ft), .. } => Some(ft),
1290 InferredType::ArraySubscript { element_type, .. } => Some(element_type),
1291 InferredType::AddressOf { inner_type } => Some(inner_type),
1292 InferredType::Dereference { pointer_type } => Some(pointer_type),
1293 InferredType::SymbolLookup { resolved_type, .. } => Some(resolved_type),
1294 InferredType::IncDec { inner_type } => Some(inner_type),
1295 InferredType::Assignment { lhs_type } => Some(lhs_type),
1296 InferredType::Comma { rhs_type } => Some(rhs_type),
1297 InferredType::Conditional { result_type, .. } => Some(result_type),
1298 InferredType::BinaryOp { result_type, .. } => Some(result_type),
1299 InferredType::UnaryArithmetic { inner_type } => Some(inner_type),
1300 InferredType::CompoundLiteral { type_name } => Some(type_name),
1301 InferredType::StmtExpr { last_expr_type } => last_expr_type.as_deref(),
1302 _ => None,
1303 }
1304 }
1305}
1306
1307impl RustTypeRepr {
1308 fn pointee_name(&self) -> Option<InternedStr> {
1313 None
1316 }
1317
1318 fn type_name(&self) -> Option<InternedStr> {
1320 None
1321 }
1322}
1323
1324impl CTypeSpecs {
1329 pub fn to_display_string(&self, interner: &crate::intern::StringInterner) -> String {
1331 match self {
1332 CTypeSpecs::Void => "void".to_string(),
1333 CTypeSpecs::Char { signed: None } => "char".to_string(),
1334 CTypeSpecs::Char { signed: Some(true) } => "signed char".to_string(),
1335 CTypeSpecs::Char { signed: Some(false) } => "unsigned char".to_string(),
1336 CTypeSpecs::Int { signed: true, size: IntSize::Short } => "short".to_string(),
1337 CTypeSpecs::Int { signed: false, size: IntSize::Short } => "unsigned short".to_string(),
1338 CTypeSpecs::Int { signed: true, size: IntSize::Int } => "int".to_string(),
1339 CTypeSpecs::Int { signed: false, size: IntSize::Int } => "unsigned int".to_string(),
1340 CTypeSpecs::Int { signed: true, size: IntSize::Long } => "long".to_string(),
1341 CTypeSpecs::Int { signed: false, size: IntSize::Long } => "unsigned long".to_string(),
1342 CTypeSpecs::Int { signed: true, size: IntSize::LongLong } => "long long".to_string(),
1343 CTypeSpecs::Int { signed: false, size: IntSize::LongLong } => "unsigned long long".to_string(),
1344 CTypeSpecs::Int { signed: true, size: IntSize::Int128 } => "__int128".to_string(),
1345 CTypeSpecs::Int { signed: false, size: IntSize::Int128 } => "unsigned __int128".to_string(),
1346 CTypeSpecs::Float => "float".to_string(),
1347 CTypeSpecs::Double { is_long: false } => "double".to_string(),
1348 CTypeSpecs::Double { is_long: true } => "long double".to_string(),
1349 CTypeSpecs::Bool => "_Bool".to_string(),
1350 CTypeSpecs::Struct { name: Some(n), is_union: false } => {
1351 format!("struct {}", interner.get(*n))
1352 }
1353 CTypeSpecs::Struct { name: None, is_union: false } => "struct".to_string(),
1354 CTypeSpecs::Struct { name: Some(n), is_union: true } => {
1355 format!("union {}", interner.get(*n))
1356 }
1357 CTypeSpecs::Struct { name: None, is_union: true } => "union".to_string(),
1358 CTypeSpecs::Enum { name: Some(n) } => format!("enum {}", interner.get(*n)),
1359 CTypeSpecs::Enum { name: None } => "enum".to_string(),
1360 CTypeSpecs::TypedefName(n) => interner.get(*n).to_string(),
1361 CTypeSpecs::UnknownTypedef(s) => s.clone(),
1362 }
1363 }
1364
1365 pub fn to_rust_string(&self, interner: &crate::intern::StringInterner) -> String {
1367 match self {
1368 CTypeSpecs::Void => "()".to_string(),
1369 CTypeSpecs::Char { signed: None } => "c_char".to_string(),
1370 CTypeSpecs::Char { signed: Some(true) } => "c_schar".to_string(),
1371 CTypeSpecs::Char { signed: Some(false) } => "c_uchar".to_string(),
1372 CTypeSpecs::Int { signed: true, size: IntSize::Short } => "c_short".to_string(),
1373 CTypeSpecs::Int { signed: false, size: IntSize::Short } => "c_ushort".to_string(),
1374 CTypeSpecs::Int { signed: true, size: IntSize::Int } => "c_int".to_string(),
1375 CTypeSpecs::Int { signed: false, size: IntSize::Int } => "c_uint".to_string(),
1376 CTypeSpecs::Int { signed: true, size: IntSize::Long } => "c_long".to_string(),
1377 CTypeSpecs::Int { signed: false, size: IntSize::Long } => "c_ulong".to_string(),
1378 CTypeSpecs::Int { signed: true, size: IntSize::LongLong } => "c_longlong".to_string(),
1379 CTypeSpecs::Int { signed: false, size: IntSize::LongLong } => "c_ulonglong".to_string(),
1380 CTypeSpecs::Int { signed: true, size: IntSize::Int128 } => "i128".to_string(),
1381 CTypeSpecs::Int { signed: false, size: IntSize::Int128 } => "u128".to_string(),
1382 CTypeSpecs::Float => "c_float".to_string(),
1383 CTypeSpecs::Double { is_long: false } => "c_double".to_string(),
1384 CTypeSpecs::Double { is_long: true } => "c_double".to_string(), CTypeSpecs::Bool => "bool".to_string(),
1386 CTypeSpecs::Struct { name: Some(n), .. } => interner.get(*n).to_string(),
1387 CTypeSpecs::Struct { name: None, .. } => "/* anonymous struct */".to_string(),
1388 CTypeSpecs::Enum { name: Some(n) } => interner.get(*n).to_string(),
1389 CTypeSpecs::Enum { name: None } => "/* anonymous enum */".to_string(),
1390 CTypeSpecs::TypedefName(n) => interner.get(*n).to_string(),
1391 CTypeSpecs::UnknownTypedef(s) => s.clone(),
1392 }
1393 }
1394}
1395
1396impl RustTypeRepr {
1397 pub fn make_outer_pointer_const(&mut self) {
1399 if let RustTypeRepr::Pointer { is_const, .. } = self {
1400 *is_const = true;
1401 }
1402 }
1403
1404 pub fn has_outer_pointer(&self) -> bool {
1406 matches!(self, RustTypeRepr::Pointer { .. })
1407 }
1408}
1409
1410impl RustTypeRepr {
1411 pub fn to_display_string(&self) -> String {
1413 match self {
1414 RustTypeRepr::CPrimitive(kind) => kind.to_string(),
1415 RustTypeRepr::RustPrimitive(kind) => kind.to_string(),
1416 RustTypeRepr::Pointer { inner, is_const: true } => {
1417 format!("*const {}", inner.to_display_string())
1418 }
1419 RustTypeRepr::Pointer { inner, is_const: false } => {
1420 format!("*mut {}", inner.to_display_string())
1421 }
1422 RustTypeRepr::Reference { inner, is_mut: true } => {
1423 format!("&mut {}", inner.to_display_string())
1424 }
1425 RustTypeRepr::Reference { inner, is_mut: false } => {
1426 format!("&{}", inner.to_display_string())
1427 }
1428 RustTypeRepr::Named(name) => name.clone(),
1429 RustTypeRepr::Option(inner) => format!("Option<{}>", inner.to_display_string()),
1430 RustTypeRepr::FnPointer { params, ret } => {
1431 let params_str: Vec<_> = params.iter().map(|p| p.to_display_string()).collect();
1432 let ret_str = ret
1433 .as_ref()
1434 .map(|r| format!(" -> {}", r.to_display_string()))
1435 .unwrap_or_default();
1436 format!("fn({}){}", params_str.join(", "), ret_str)
1437 }
1438 RustTypeRepr::Unit => "()".to_string(),
1439 RustTypeRepr::Unknown(s) => s.clone(),
1440 }
1441 }
1442}
1443
1444impl fmt::Display for CPrimitiveKind {
1445 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1446 let s = match self {
1447 CPrimitiveKind::CChar => "c_char",
1448 CPrimitiveKind::CSchar => "c_schar",
1449 CPrimitiveKind::CUchar => "c_uchar",
1450 CPrimitiveKind::CShort => "c_short",
1451 CPrimitiveKind::CUshort => "c_ushort",
1452 CPrimitiveKind::CInt => "c_int",
1453 CPrimitiveKind::CUint => "c_uint",
1454 CPrimitiveKind::CLong => "c_long",
1455 CPrimitiveKind::CUlong => "c_ulong",
1456 CPrimitiveKind::CLongLong => "c_longlong",
1457 CPrimitiveKind::CUlongLong => "c_ulonglong",
1458 CPrimitiveKind::CFloat => "c_float",
1459 CPrimitiveKind::CDouble => "c_double",
1460 };
1461 write!(f, "{}", s)
1462 }
1463}
1464
1465impl fmt::Display for RustPrimitiveKind {
1466 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1467 let s = match self {
1468 RustPrimitiveKind::I8 => "i8",
1469 RustPrimitiveKind::I16 => "i16",
1470 RustPrimitiveKind::I32 => "i32",
1471 RustPrimitiveKind::I64 => "i64",
1472 RustPrimitiveKind::I128 => "i128",
1473 RustPrimitiveKind::Isize => "isize",
1474 RustPrimitiveKind::U8 => "u8",
1475 RustPrimitiveKind::U16 => "u16",
1476 RustPrimitiveKind::U32 => "u32",
1477 RustPrimitiveKind::U64 => "u64",
1478 RustPrimitiveKind::U128 => "u128",
1479 RustPrimitiveKind::Usize => "usize",
1480 RustPrimitiveKind::F32 => "f32",
1481 RustPrimitiveKind::F64 => "f64",
1482 RustPrimitiveKind::Bool => "bool",
1483 };
1484 write!(f, "{}", s)
1485 }
1486}
1487
1488impl InferredType {
1489 pub fn to_display_string(&self, interner: &crate::intern::StringInterner) -> String {
1491 match self {
1492 InferredType::IntLiteral => "int".to_string(),
1493 InferredType::UIntLiteral => "unsigned int".to_string(),
1494 InferredType::FloatLiteral => "double".to_string(),
1495 InferredType::CharLiteral => "int".to_string(),
1496 InferredType::StringLiteral => "char *".to_string(),
1497 InferredType::SymbolLookup { resolved_type, .. } => {
1498 resolved_type.to_display_string(interner)
1499 }
1500 InferredType::ThxDefault => "*mut PerlInterpreter".to_string(),
1501 InferredType::BinaryOp { result_type, .. } => result_type.to_display_string(interner),
1502 InferredType::UnaryArithmetic { inner_type } => inner_type.to_display_string(interner),
1503 InferredType::LogicalNot => "int".to_string(),
1504 InferredType::AddressOf { inner_type } => {
1505 format!("{} *", inner_type.to_display_string(interner))
1506 }
1507 InferredType::Dereference { pointer_type } => {
1508 let s = pointer_type.to_display_string(interner);
1509 s.trim_end_matches(" *").to_string()
1510 }
1511 InferredType::IncDec { inner_type } => inner_type.to_display_string(interner),
1512 InferredType::MemberAccess { field_type: Some(ft), .. } => {
1513 ft.to_display_string(interner)
1514 }
1515 InferredType::MemberAccess { base_type, member, .. } => {
1516 format!("{}.{}", base_type, interner.get(*member))
1517 }
1518 InferredType::PtrMemberAccess { field_type: Some(ft), .. } => {
1519 ft.to_display_string(interner)
1520 }
1521 InferredType::PtrMemberAccess { base_type, member, .. } => {
1522 format!("{}->{}", base_type, interner.get(*member))
1523 }
1524 InferredType::ArraySubscript { element_type, .. } => {
1525 element_type.to_display_string(interner)
1526 }
1527 InferredType::Conditional { result_type, .. } => {
1528 result_type.to_display_string(interner)
1529 }
1530 InferredType::Comma { rhs_type } => rhs_type.to_display_string(interner),
1531 InferredType::Assignment { lhs_type } => lhs_type.to_display_string(interner),
1532 InferredType::Cast { target_type } => target_type.to_display_string(interner),
1533 InferredType::Sizeof | InferredType::Alignof => "unsigned long".to_string(),
1534 InferredType::CompoundLiteral { type_name } => type_name.to_display_string(interner),
1535 InferredType::StmtExpr { last_expr_type: Some(t) } => t.to_display_string(interner),
1536 InferredType::StmtExpr { last_expr_type: None } => "void".to_string(),
1537 InferredType::Assert => "void".to_string(),
1538 InferredType::FunctionReturn { func_name } => {
1539 format!("{}()", interner.get(*func_name))
1540 }
1541 }
1542 }
1543
1544 pub fn to_rust_string(&self, interner: &crate::intern::StringInterner) -> String {
1546 match self {
1547 InferredType::IntLiteral => "c_int".to_string(),
1548 InferredType::UIntLiteral => "c_uint".to_string(),
1549 InferredType::FloatLiteral => "c_double".to_string(),
1550 InferredType::CharLiteral => "c_int".to_string(),
1551 InferredType::StringLiteral => "*const c_char".to_string(),
1552 InferredType::SymbolLookup { resolved_type, .. } => {
1553 resolved_type.to_rust_string(interner)
1554 }
1555 InferredType::ThxDefault => "*mut PerlInterpreter".to_string(),
1556 InferredType::BinaryOp { result_type, .. } => result_type.to_rust_string(interner),
1557 InferredType::UnaryArithmetic { inner_type } => inner_type.to_rust_string(interner),
1558 InferredType::LogicalNot => "c_int".to_string(),
1559 InferredType::AddressOf { inner_type } => {
1560 format!("*mut {}", inner_type.to_rust_string(interner))
1561 }
1562 InferredType::Dereference { pointer_type } => {
1563 let s = pointer_type.to_rust_string(interner);
1564 s.strip_prefix("*mut ").or_else(|| s.strip_prefix("*const "))
1566 .unwrap_or(&s).to_string()
1567 }
1568 InferredType::IncDec { inner_type } => inner_type.to_rust_string(interner),
1569 InferredType::MemberAccess { field_type: Some(ft), .. } => {
1570 ft.to_rust_string(interner)
1571 }
1572 InferredType::MemberAccess { base_type, member, .. } => {
1573 format!("/* {}.{} */", base_type, interner.get(*member))
1574 }
1575 InferredType::PtrMemberAccess { field_type: Some(ft), .. } => {
1576 ft.to_rust_string(interner)
1577 }
1578 InferredType::PtrMemberAccess { base_type, member, .. } => {
1579 format!("/* {}->{} */", base_type, interner.get(*member))
1580 }
1581 InferredType::ArraySubscript { element_type, .. } => {
1582 element_type.to_rust_string(interner)
1583 }
1584 InferredType::Conditional { result_type, .. } => {
1585 result_type.to_rust_string(interner)
1586 }
1587 InferredType::Comma { rhs_type } => rhs_type.to_rust_string(interner),
1588 InferredType::Assignment { lhs_type } => lhs_type.to_rust_string(interner),
1589 InferredType::Cast { target_type } => target_type.to_rust_string(interner),
1590 InferredType::Sizeof | InferredType::Alignof => "c_ulong".to_string(),
1591 InferredType::CompoundLiteral { type_name } => type_name.to_rust_string(interner),
1592 InferredType::StmtExpr { last_expr_type: Some(t) } => t.to_rust_string(interner),
1593 InferredType::StmtExpr { last_expr_type: None } => "()".to_string(),
1594 InferredType::Assert => "()".to_string(),
1595 InferredType::FunctionReturn { func_name } => {
1596 format!("/* {}() ret */", interner.get(*func_name))
1597 }
1598 }
1599 }
1600}
1601
1602#[cfg(test)]
1607mod tests {
1608 use super::*;
1609
1610 #[test]
1611 fn test_rust_type_repr_from_string() {
1612 assert!(matches!(
1613 RustTypeRepr::from_type_string("c_int"),
1614 RustTypeRepr::CPrimitive(CPrimitiveKind::CInt)
1615 ));
1616
1617 assert!(matches!(
1618 RustTypeRepr::from_type_string("i32"),
1619 RustTypeRepr::RustPrimitive(RustPrimitiveKind::I32)
1620 ));
1621
1622 assert!(matches!(
1623 RustTypeRepr::from_type_string("()"),
1624 RustTypeRepr::Unit
1625 ));
1626
1627 if let RustTypeRepr::Pointer { inner, is_const: false } =
1628 RustTypeRepr::from_type_string("*mut SV")
1629 {
1630 assert!(matches!(*inner, RustTypeRepr::Named(ref n) if n == "SV"));
1631 } else {
1632 panic!("Expected *mut SV");
1633 }
1634
1635 if let RustTypeRepr::Pointer { inner, is_const: true } =
1636 RustTypeRepr::from_type_string("*const c_char")
1637 {
1638 assert!(matches!(*inner, RustTypeRepr::CPrimitive(CPrimitiveKind::CChar)));
1639 } else {
1640 panic!("Expected *const c_char");
1641 }
1642 }
1643
1644 #[test]
1645 fn test_rust_type_repr_from_string_with_spaces() {
1646 if let RustTypeRepr::Pointer { inner, is_const: false } =
1648 RustTypeRepr::from_type_string("* mut SV")
1649 {
1650 assert!(matches!(*inner, RustTypeRepr::Named(ref n) if n == "SV"));
1651 } else {
1652 panic!("Expected * mut SV");
1653 }
1654 }
1655
1656 #[test]
1657 fn test_c_primitive_display() {
1658 assert_eq!(CPrimitiveKind::CInt.to_string(), "c_int");
1659 assert_eq!(CPrimitiveKind::CUlong.to_string(), "c_ulong");
1660 }
1661
1662 #[test]
1663 fn test_rust_primitive_display() {
1664 assert_eq!(RustPrimitiveKind::I32.to_string(), "i32");
1665 assert_eq!(RustPrimitiveKind::Usize.to_string(), "usize");
1666 }
1667
1668 fn make_void_ptr() -> TypeRepr {
1671 TypeRepr::CType {
1672 specs: CTypeSpecs::Void,
1673 derived: vec![CDerivedType::Pointer {
1674 is_const: false,
1675 is_volatile: false,
1676 is_restrict: false,
1677 }],
1678 source: CTypeSource::Apidoc { raw: "void *".to_string() },
1679 }
1680 }
1681
1682 fn make_concrete_ptr() -> TypeRepr {
1683 TypeRepr::CType {
1684 specs: CTypeSpecs::Char { signed: None },
1685 derived: vec![CDerivedType::Pointer {
1686 is_const: false,
1687 is_volatile: false,
1688 is_restrict: false,
1689 }],
1690 source: CTypeSource::Apidoc { raw: "char *".to_string() },
1691 }
1692 }
1693
1694 #[test]
1695 fn test_void_pointer_structural() {
1696 let vp = make_void_ptr();
1697 assert!(vp.is_pointer_type());
1698 assert!(vp.is_void_pointer());
1699 assert!(!vp.is_concrete_pointer());
1700 }
1701
1702 #[test]
1703 fn test_concrete_pointer_structural() {
1704 let cp = make_concrete_ptr();
1705 assert!(cp.is_pointer_type());
1706 assert!(!cp.is_void_pointer());
1707 assert!(cp.is_concrete_pointer());
1708 }
1709
1710 #[test]
1711 fn test_inferred_member_access_pointer_recursive() {
1712 let inner = make_concrete_ptr();
1716 let inferred = TypeRepr::Inferred(InferredType::MemberAccess {
1717 base_type: "xpvcv".to_string(),
1718 member: crate::intern::StringInterner::new().intern("foo"),
1719 field_type: Some(Box::new(inner)),
1720 });
1721 assert!(inferred.is_pointer_type());
1722 assert!(!inferred.is_void_pointer());
1723 assert!(inferred.is_concrete_pointer());
1724 assert!(!inferred.has_outer_pointer());
1726 }
1727}