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 Propagated { raw: String },
86}
87
88#[derive(Debug, Clone, PartialEq, Eq)]
94pub enum CTypeSpecs {
95 Void,
97 Char { signed: Option<bool> },
99 Int { signed: bool, size: IntSize },
101 Float,
103 Double { is_long: bool },
105 Bool,
107 Struct { name: Option<InternedStr>, is_union: bool },
109 Enum { name: Option<InternedStr> },
111 TypedefName(InternedStr),
113 UnknownTypedef(String),
115}
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq)]
119pub enum IntSize {
120 Short,
122 Int,
124 Long,
126 LongLong,
128 Int128,
130}
131
132#[derive(Debug, Clone, PartialEq, Eq)]
134pub enum CDerivedType {
135 Pointer {
137 is_const: bool,
138 is_volatile: bool,
139 is_restrict: bool,
140 },
141 Array { size: Option<usize> },
143 Function {
145 params: Vec<CTypeSpecs>,
146 variadic: bool,
147 },
148}
149
150#[derive(Debug, Clone, PartialEq, Eq)]
156pub enum RustTypeRepr {
157 CPrimitive(CPrimitiveKind),
159 RustPrimitive(RustPrimitiveKind),
161 Pointer {
163 inner: Box<RustTypeRepr>,
164 is_const: bool,
165 },
166 Reference {
168 inner: Box<RustTypeRepr>,
169 is_mut: bool,
170 },
171 Named(String),
173 Option(Box<RustTypeRepr>),
175 FnPointer {
177 params: Vec<RustTypeRepr>,
178 ret: Option<Box<RustTypeRepr>>,
179 },
180 Unit,
182 Unknown(String),
184}
185
186#[derive(Debug, Clone, Copy, PartialEq, Eq)]
188pub enum CPrimitiveKind {
189 CChar,
190 CSchar,
191 CUchar,
192 CShort,
193 CUshort,
194 CInt,
195 CUint,
196 CLong,
197 CUlong,
198 CLongLong,
199 CUlongLong,
200 CFloat,
201 CDouble,
202}
203
204#[derive(Debug, Clone, Copy, PartialEq, Eq)]
206pub enum RustPrimitiveKind {
207 I8,
208 I16,
209 I32,
210 I64,
211 I128,
212 Isize,
213 U8,
214 U16,
215 U32,
216 U64,
217 U128,
218 Usize,
219 F32,
220 F64,
221 Bool,
222}
223
224#[derive(Debug, Clone)]
230pub enum InferredType {
231 IntLiteral,
234 UIntLiteral,
236 FloatLiteral,
238 CharLiteral,
240 StringLiteral,
242
243 SymbolLookup {
246 name: InternedStr,
247 resolved_type: Box<TypeRepr>,
249 },
250 ThxDefault,
252
253 BinaryOp {
256 op: BinOp,
257 result_type: Box<TypeRepr>,
259 },
260 UnaryArithmetic {
262 inner_type: Box<TypeRepr>,
264 },
265 LogicalNot,
267 AddressOf { inner_type: Box<TypeRepr> },
269 Dereference { pointer_type: Box<TypeRepr> },
271 IncDec { inner_type: Box<TypeRepr> },
273
274 MemberAccess {
277 base_type: String,
278 member: InternedStr,
279 field_type: Option<Box<TypeRepr>>,
281 },
282 PtrMemberAccess {
284 base_type: String,
285 member: InternedStr,
286 field_type: Option<Box<TypeRepr>>,
288 used_consistent_type: bool,
290 },
291
292 ArraySubscript {
295 base_type: Box<TypeRepr>,
296 element_type: Box<TypeRepr>,
298 },
299
300 Conditional {
303 then_type: Box<TypeRepr>,
304 else_type: Box<TypeRepr>,
305 result_type: Box<TypeRepr>,
307 },
308 Comma {
310 rhs_type: Box<TypeRepr>,
312 },
313 Assignment {
315 lhs_type: Box<TypeRepr>,
317 },
318
319 Cast { target_type: Box<TypeRepr> },
322 Sizeof,
324 Alignof,
326 CompoundLiteral { type_name: Box<TypeRepr> },
328
329 StmtExpr {
332 last_expr_type: Option<Box<TypeRepr>>,
334 },
335 Assert,
337 FunctionReturn { func_name: InternedStr },
339}
340
341impl CTypeSpecs {
346 pub fn from_decl_specs(specs: &crate::ast::DeclSpecs, _interner: &crate::intern::StringInterner) -> Self {
348 use crate::ast::TypeSpec;
349
350 let mut has_signed = false;
351 let mut has_unsigned = false;
352 let mut has_short = false;
353 let mut has_long: u8 = 0;
354 let mut base_type: Option<CTypeSpecs> = None;
355
356 for type_spec in &specs.type_specs {
357 match type_spec {
358 TypeSpec::Void => base_type = Some(CTypeSpecs::Void),
359 TypeSpec::Char => {
360 if base_type.is_none() {
362 base_type = Some(CTypeSpecs::Char { signed: None });
363 }
364 }
365 TypeSpec::Short => has_short = true,
366 TypeSpec::Int => {
367 if base_type.is_none() {
368 base_type = Some(CTypeSpecs::Int {
369 signed: true,
370 size: IntSize::Int,
371 });
372 }
373 }
374 TypeSpec::Long => has_long += 1,
375 TypeSpec::Float => base_type = Some(CTypeSpecs::Float),
376 TypeSpec::Double => base_type = Some(CTypeSpecs::Double { is_long: false }),
377 TypeSpec::Signed => has_signed = true,
378 TypeSpec::Unsigned => has_unsigned = true,
379 TypeSpec::Bool => base_type = Some(CTypeSpecs::Bool),
380 TypeSpec::Int128 => {
381 base_type = Some(CTypeSpecs::Int {
382 signed: !has_unsigned,
383 size: IntSize::Int128,
384 });
385 }
386 TypeSpec::Struct(s) => {
387 base_type = Some(CTypeSpecs::Struct {
388 name: s.name,
389 is_union: false,
390 });
391 }
392 TypeSpec::Union(s) => {
393 base_type = Some(CTypeSpecs::Struct {
394 name: s.name,
395 is_union: true,
396 });
397 }
398 TypeSpec::Enum(e) => {
399 base_type = Some(CTypeSpecs::Enum { name: e.name });
400 }
401 TypeSpec::TypedefName(name) => {
402 base_type = Some(CTypeSpecs::TypedefName(*name));
403 }
404 _ => {}
405 }
406 }
407
408 if has_short {
410 return CTypeSpecs::Int {
411 signed: !has_unsigned,
412 size: IntSize::Short,
413 };
414 }
415
416 if has_long >= 2 {
417 return CTypeSpecs::Int {
418 signed: !has_unsigned,
419 size: IntSize::LongLong,
420 };
421 }
422
423 if has_long == 1 {
424 if let Some(CTypeSpecs::Double { .. }) = base_type {
425 return CTypeSpecs::Double { is_long: true };
426 }
427 return CTypeSpecs::Int {
428 signed: !has_unsigned,
429 size: IntSize::Long,
430 };
431 }
432
433 if let Some(CTypeSpecs::Char { .. }) = base_type {
435 if has_signed {
436 return CTypeSpecs::Char { signed: Some(true) };
437 } else if has_unsigned {
438 return CTypeSpecs::Char { signed: Some(false) };
439 }
440 return CTypeSpecs::Char { signed: None };
441 }
442
443 if has_unsigned && base_type.is_none() {
445 return CTypeSpecs::Int {
446 signed: false,
447 size: IntSize::Int,
448 };
449 }
450 if has_signed && base_type.is_none() {
451 return CTypeSpecs::Int {
452 signed: true,
453 size: IntSize::Int,
454 };
455 }
456
457 if has_unsigned {
459 if let Some(CTypeSpecs::Int { size, .. }) = base_type {
460 return CTypeSpecs::Int {
461 signed: false,
462 size,
463 };
464 }
465 }
466
467 base_type.unwrap_or(CTypeSpecs::Int {
468 signed: true,
469 size: IntSize::Int,
470 })
471 }
472}
473
474impl CDerivedType {
475 pub fn from_derived_decls(derived: &[crate::ast::DerivedDecl]) -> Vec<Self> {
477 use crate::ast::ExprKind;
478
479 derived
480 .iter()
481 .map(|d| match d {
482 crate::ast::DerivedDecl::Pointer(quals) => CDerivedType::Pointer {
483 is_const: quals.is_const,
484 is_volatile: quals.is_volatile,
485 is_restrict: quals.is_restrict,
486 },
487 crate::ast::DerivedDecl::Array(array_decl) => {
488 let size = array_decl.size.as_ref().and_then(|expr| {
490 match &expr.kind {
491 ExprKind::IntLit(n) => Some(*n as usize),
492 ExprKind::UIntLit(n) => Some(*n as usize),
493 _ => None,
494 }
495 });
496 CDerivedType::Array { size }
497 }
498 crate::ast::DerivedDecl::Function(_params) => {
499 CDerivedType::Function {
501 params: vec![],
502 variadic: false,
503 }
504 }
505 })
506 .collect()
507 }
508}
509
510impl RustTypeRepr {
511 pub fn from_type_string(s: &str) -> Self {
513 let s = s.trim();
514
515 if s == "()" {
517 return RustTypeRepr::Unit;
518 }
519
520 if let Some(rest) = s.strip_prefix("*mut ") {
522 return RustTypeRepr::Pointer {
523 inner: Box::new(Self::from_type_string(rest)),
524 is_const: false,
525 };
526 }
527 if let Some(rest) = s.strip_prefix("* mut ") {
528 return RustTypeRepr::Pointer {
529 inner: Box::new(Self::from_type_string(rest)),
530 is_const: false,
531 };
532 }
533 if let Some(rest) = s.strip_prefix("*const ") {
534 return RustTypeRepr::Pointer {
535 inner: Box::new(Self::from_type_string(rest)),
536 is_const: true,
537 };
538 }
539 if let Some(rest) = s.strip_prefix("* const ") {
540 return RustTypeRepr::Pointer {
541 inner: Box::new(Self::from_type_string(rest)),
542 is_const: true,
543 };
544 }
545
546 if let Some(rest) = s.strip_prefix("&mut ") {
548 return RustTypeRepr::Reference {
549 inner: Box::new(Self::from_type_string(rest)),
550 is_mut: true,
551 };
552 }
553 if let Some(rest) = s.strip_prefix("& mut ") {
554 return RustTypeRepr::Reference {
555 inner: Box::new(Self::from_type_string(rest)),
556 is_mut: true,
557 };
558 }
559 if let Some(rest) = s.strip_prefix('&') {
560 return RustTypeRepr::Reference {
561 inner: Box::new(Self::from_type_string(rest.trim())),
562 is_mut: false,
563 };
564 }
565
566 if let Some(kind) = Self::parse_c_primitive(s) {
568 return RustTypeRepr::CPrimitive(kind);
569 }
570
571 if let Some(kind) = Self::parse_rust_primitive(s) {
573 return RustTypeRepr::RustPrimitive(kind);
574 }
575
576 if s.starts_with("Option<") || s.starts_with(":: std :: option :: Option<") {
578 if let Some(inner) = Self::extract_generic_param(s, "Option") {
579 return RustTypeRepr::Option(Box::new(Self::from_type_string(&inner)));
580 }
581 }
582
583 if s.chars().next().map(|c| c.is_alphabetic() || c == '_').unwrap_or(false) {
585 let name = s.split("::").last().unwrap_or(s).trim();
587 return RustTypeRepr::Named(name.to_string());
588 }
589
590 RustTypeRepr::Unknown(s.to_string())
592 }
593
594 fn parse_c_primitive(s: &str) -> Option<CPrimitiveKind> {
596 let s = s.trim();
598 let name = if s.contains("::") {
599 s.split("::").last()?.trim()
600 } else {
601 s
602 };
603
604 match name {
605 "c_char" => Some(CPrimitiveKind::CChar),
606 "c_schar" => Some(CPrimitiveKind::CSchar),
607 "c_uchar" => Some(CPrimitiveKind::CUchar),
608 "c_short" => Some(CPrimitiveKind::CShort),
609 "c_ushort" => Some(CPrimitiveKind::CUshort),
610 "c_int" => Some(CPrimitiveKind::CInt),
611 "c_uint" => Some(CPrimitiveKind::CUint),
612 "c_long" => Some(CPrimitiveKind::CLong),
613 "c_ulong" => Some(CPrimitiveKind::CUlong),
614 "c_longlong" => Some(CPrimitiveKind::CLongLong),
615 "c_ulonglong" => Some(CPrimitiveKind::CUlongLong),
616 "c_float" => Some(CPrimitiveKind::CFloat),
617 "c_double" => Some(CPrimitiveKind::CDouble),
618 _ => None,
619 }
620 }
621
622 fn parse_rust_primitive(s: &str) -> Option<RustPrimitiveKind> {
624 match s.trim() {
625 "i8" => Some(RustPrimitiveKind::I8),
626 "i16" => Some(RustPrimitiveKind::I16),
627 "i32" => Some(RustPrimitiveKind::I32),
628 "i64" => Some(RustPrimitiveKind::I64),
629 "i128" => Some(RustPrimitiveKind::I128),
630 "isize" => Some(RustPrimitiveKind::Isize),
631 "u8" => Some(RustPrimitiveKind::U8),
632 "u16" => Some(RustPrimitiveKind::U16),
633 "u32" => Some(RustPrimitiveKind::U32),
634 "u64" => Some(RustPrimitiveKind::U64),
635 "u128" => Some(RustPrimitiveKind::U128),
636 "usize" => Some(RustPrimitiveKind::Usize),
637 "f32" => Some(RustPrimitiveKind::F32),
638 "f64" => Some(RustPrimitiveKind::F64),
639 "bool" => Some(RustPrimitiveKind::Bool),
640 _ => None,
641 }
642 }
643
644 fn extract_generic_param(s: &str, type_name: &str) -> Option<String> {
646 let start = s.find(&format!("{}<", type_name))?;
648 let after_open = start + type_name.len() + 1;
649 let content = &s[after_open..];
650
651 let mut depth = 1;
653 let mut end = 0;
654 for (i, c) in content.char_indices() {
655 match c {
656 '<' => depth += 1,
657 '>' => {
658 depth -= 1;
659 if depth == 0 {
660 end = i;
661 break;
662 }
663 }
664 _ => {}
665 }
666 }
667
668 if end > 0 {
669 Some(content[..end].trim().to_string())
670 } else {
671 None
672 }
673 }
674}
675
676impl TypeRepr {
677 pub fn source_display(&self) -> &'static str {
679 match self {
680 TypeRepr::CType { source, .. } => match source {
681 CTypeSource::Header => "c-header",
682 CTypeSource::Apidoc { .. } => "apidoc",
683 CTypeSource::InlineFn { .. } => "inline-fn",
684 CTypeSource::Parser => "parser",
685 CTypeSource::FieldInference { .. } => "field-inference",
686 CTypeSource::Cast => "cast",
687 CTypeSource::SvFamilyCast => "sv-family-cast",
688 CTypeSource::CommonMacroFieldInference => "common-macro-field-inference",
689 },
690 TypeRepr::RustType { .. } => "rust-bindings",
691 TypeRepr::Inferred(_) => "inferred",
692 }
693 }
694
695 pub fn is_fn_param_source(&self) -> bool {
701 matches!(self, TypeRepr::RustType { source: RustTypeSource::FnParam { .. }, .. })
702 }
703
704 pub fn confidence_tier(&self) -> u8 {
711 match self {
712 TypeRepr::RustType { source, .. } => match source {
713 RustTypeSource::FnParam { .. }
714 | RustTypeSource::FnReturn { .. }
715 | RustTypeSource::Const { .. } => 1,
716 RustTypeSource::Parsed { .. } => 3,
717 RustTypeSource::Propagated { .. } => 4,
718 },
719 TypeRepr::CType { source, .. } => match source {
720 CTypeSource::InlineFn { .. } | CTypeSource::Header => 2,
721 CTypeSource::Apidoc { .. }
722 | CTypeSource::CommonMacroFieldInference => 3,
723 CTypeSource::Cast
724 | CTypeSource::SvFamilyCast
725 | CTypeSource::FieldInference { .. }
726 | CTypeSource::Parser => 4,
727 },
728 TypeRepr::Inferred(_) => 4,
729 }
730 }
731
732 pub fn is_void(&self) -> bool {
733 match self {
734 TypeRepr::CType { specs, derived, .. } => {
735 derived.is_empty() && matches!(specs, CTypeSpecs::Void)
737 }
738 TypeRepr::RustType { repr, .. } => {
739 matches!(repr, RustTypeRepr::Unit)
740 }
741 TypeRepr::Inferred(inferred) => {
742 match inferred {
743 InferredType::SymbolLookup { resolved_type, .. } => {
744 resolved_type.is_void()
745 }
746 _ => false,
747 }
748 }
749 }
750 }
751
752 pub fn make_outer_pointer_mut(&mut self) {
755 match self {
756 TypeRepr::CType { derived, .. } => {
757 for d in derived.iter_mut().rev() {
758 if let CDerivedType::Pointer { is_const, .. } = d {
759 *is_const = false;
760 return;
761 }
762 }
763 }
764 TypeRepr::RustType { repr, .. } => {
765 if let RustTypeRepr::Pointer { is_const, .. } = repr {
766 *is_const = false;
767 }
768 }
769 _ => {}
770 }
771 }
772
773 pub fn make_outer_pointer_const(&mut self) {
774 match self {
775 TypeRepr::CType { derived, .. } => {
776 for d in derived.iter_mut().rev() {
778 if let CDerivedType::Pointer { is_const, .. } = d {
779 *is_const = true;
780 return;
781 }
782 }
783 }
784 TypeRepr::RustType { repr, .. } => {
785 repr.make_outer_pointer_const();
786 }
787 TypeRepr::Inferred(inferred) => {
788 match inferred {
789 InferredType::SymbolLookup { resolved_type, .. } => {
790 resolved_type.make_outer_pointer_const();
791 }
792 InferredType::Cast { target_type } => {
793 target_type.make_outer_pointer_const();
794 }
795 _ => {}
796 }
797 }
798 }
799 }
800
801 pub fn is_pointer_type(&self) -> bool {
808 match self {
809 TypeRepr::CType { derived, .. } => {
810 derived.iter().any(|d| matches!(d, CDerivedType::Pointer { .. }))
811 }
812 TypeRepr::RustType { repr, .. } => repr.has_outer_pointer(),
813 TypeRepr::Inferred(inferred) => inferred
814 .resolved_type()
815 .is_some_and(|t| t.is_pointer_type()),
816 }
817 }
818
819 pub fn is_void_pointer(&self) -> bool {
825 match self {
826 TypeRepr::CType { specs, derived, .. } => {
827 derived.iter().any(|d| matches!(d, CDerivedType::Pointer { .. }))
828 && matches!(specs, CTypeSpecs::Void)
829 }
830 TypeRepr::RustType { repr, .. } => match repr {
831 RustTypeRepr::Pointer { inner, .. } => {
832 matches!(inner.as_ref(), RustTypeRepr::Unit)
833 || matches!(inner.as_ref(), RustTypeRepr::Named(n) if n == "c_void")
834 }
835 _ => false,
836 },
837 TypeRepr::Inferred(inferred) => inferred
838 .resolved_type()
839 .is_some_and(|t| t.is_void_pointer()),
840 }
841 }
842
843 pub fn is_concrete_pointer(&self) -> bool {
845 self.is_pointer_type() && !self.is_void_pointer()
846 }
847
848 pub fn has_outer_pointer(&self) -> bool {
853 match self {
854 TypeRepr::CType { derived, .. } => {
855 derived.iter().any(|d| matches!(d, CDerivedType::Pointer { .. }))
856 }
857 TypeRepr::RustType { repr, .. } => repr.has_outer_pointer(),
858 _ => false,
859 }
860 }
861
862 pub fn from_apidoc_string(s: &str, interner: &crate::intern::StringInterner) -> Self {
864 let (specs, derived) = Self::parse_c_type_string(s, interner);
866 TypeRepr::CType {
867 specs,
868 derived,
869 source: CTypeSource::Apidoc { raw: s.to_string() },
870 }
871 }
872
873 pub fn from_rust_string(s: &str) -> Self {
878 let repr = RustTypeRepr::from_type_string(s);
879 TypeRepr::RustType {
880 repr,
881 source: RustTypeSource::Parsed {
882 raw: s.to_string(),
883 },
884 }
885 }
886
887 pub fn from_rust_string_propagated(s: &str) -> Self {
892 let repr = RustTypeRepr::from_type_string(s);
893 TypeRepr::RustType {
894 repr,
895 source: RustTypeSource::Propagated {
896 raw: s.to_string(),
897 },
898 }
899 }
900
901 pub fn from_unified_type(
914 ut: &crate::unified_type::UnifiedType,
915 interner: &crate::intern::StringInterner,
916 ) -> Self {
917 let raw = ut.to_rust_string();
918 if let Some((specs, derived)) = unified_to_c(ut, interner) {
919 return TypeRepr::CType {
920 specs,
921 derived,
922 source: CTypeSource::Apidoc { raw },
923 };
924 }
925 TypeRepr::RustType {
926 repr: RustTypeRepr::Unknown(raw.clone()),
927 source: RustTypeSource::Parsed { raw },
928 }
929 }
930
931 pub fn from_decl(
936 specs: &crate::ast::DeclSpecs,
937 declarator: &crate::ast::Declarator,
938 _interner: &crate::intern::StringInterner,
939 ) -> Self {
940 let c_specs = CTypeSpecs::from_decl_specs(specs, _interner);
941 let derived = CDerivedType::from_derived_decls(&declarator.derived);
942 TypeRepr::CType {
943 specs: c_specs,
944 derived,
945 source: CTypeSource::Header,
946 }
947 }
948
949 pub fn from_type_name(
954 type_name: &crate::ast::TypeName,
955 interner: &crate::intern::StringInterner,
956 ) -> Self {
957 let c_specs = CTypeSpecs::from_decl_specs(&type_name.specs, interner);
958 let derived = type_name.declarator
959 .as_ref()
960 .map(|d| CDerivedType::from_derived_decls(&d.derived))
961 .unwrap_or_default();
962 TypeRepr::CType {
963 specs: c_specs,
964 derived,
965 source: CTypeSource::Parser,
966 }
967 }
968
969 pub fn from_c_type_string(
977 s: &str,
978 interner: &crate::intern::StringInterner,
979 files: &crate::source::FileRegistry,
980 typedefs: &std::collections::HashSet<crate::intern::InternedStr>,
981 ) -> Self {
982 use crate::parser::parse_type_from_string;
983
984 match parse_type_from_string(s, interner, files, typedefs) {
985 Ok(type_name) => Self::from_type_name(&type_name, interner),
986 Err(_) => {
987 let (specs, derived) = Self::parse_c_type_string(s, interner);
989 TypeRepr::CType {
990 specs,
991 derived,
992 source: CTypeSource::Apidoc { raw: s.to_string() },
993 }
994 }
995 }
996 }
997
998 fn parse_c_type_string(s: &str, interner: &crate::intern::StringInterner) -> (CTypeSpecs, Vec<CDerivedType>) {
1000 let s = s.trim();
1001
1002 let mut prefix_pointers: Vec<bool> = Vec::new(); let mut current = s;
1008 loop {
1009 if let Some(rest) = current.strip_prefix("*mut ") {
1010 prefix_pointers.push(false);
1011 current = rest.trim();
1012 } else if let Some(rest) = current.strip_prefix("*const ") {
1013 prefix_pointers.push(true);
1014 current = rest.trim();
1015 } else {
1016 break;
1017 }
1018 }
1019
1020 let mut ptr_count = 0;
1022 let mut is_const = false;
1023 let mut base = current;
1024
1025 while base.ends_with('*') {
1027 ptr_count += 1;
1028 base = base[..base.len() - 1].trim();
1029 }
1030
1031 if base.starts_with("const ") {
1033 is_const = true;
1034 base = base[6..].trim();
1035 }
1036 if base.ends_with(" const") {
1037 is_const = true;
1038 base = base[..base.len() - 6].trim();
1039 }
1040
1041 let specs = Self::parse_c_base_type(base, interner);
1043
1044 let mut derived: Vec<CDerivedType> = Vec::with_capacity(prefix_pointers.len() + ptr_count);
1049 for is_const_p in prefix_pointers.iter().rev() {
1050 derived.push(CDerivedType::Pointer {
1051 is_const: *is_const_p,
1052 is_volatile: false,
1053 is_restrict: false,
1054 });
1055 }
1056 for i in 0..ptr_count {
1057 derived.push(CDerivedType::Pointer {
1058 is_const: i == 0 && is_const,
1059 is_volatile: false,
1060 is_restrict: false,
1061 });
1062 }
1063
1064 (specs, derived)
1065 }
1066
1067 fn parse_c_base_type(s: &str, interner: &crate::intern::StringInterner) -> CTypeSpecs {
1069 match s {
1070 "void" => CTypeSpecs::Void,
1071 "char" => CTypeSpecs::Char { signed: None },
1072 "signed char" => CTypeSpecs::Char { signed: Some(true) },
1073 "unsigned char" => CTypeSpecs::Char { signed: Some(false) },
1074 "short" | "short int" | "signed short" | "signed short int" => {
1075 CTypeSpecs::Int { signed: true, size: IntSize::Short }
1076 }
1077 "unsigned short" | "unsigned short int" => {
1078 CTypeSpecs::Int { signed: false, size: IntSize::Short }
1079 }
1080 "int" | "signed" | "signed int" => {
1081 CTypeSpecs::Int { signed: true, size: IntSize::Int }
1082 }
1083 "unsigned" | "unsigned int" => {
1084 CTypeSpecs::Int { signed: false, size: IntSize::Int }
1085 }
1086 "long" | "long int" | "signed long" | "signed long int" => {
1087 CTypeSpecs::Int { signed: true, size: IntSize::Long }
1088 }
1089 "unsigned long" | "unsigned long int" => {
1090 CTypeSpecs::Int { signed: false, size: IntSize::Long }
1091 }
1092 "long long" | "long long int" | "signed long long" | "signed long long int" => {
1093 CTypeSpecs::Int { signed: true, size: IntSize::LongLong }
1094 }
1095 "unsigned long long" | "unsigned long long int" => {
1096 CTypeSpecs::Int { signed: false, size: IntSize::LongLong }
1097 }
1098 "float" => CTypeSpecs::Float,
1099 "double" => CTypeSpecs::Double { is_long: false },
1100 "long double" => CTypeSpecs::Double { is_long: true },
1101 "_Bool" | "bool" => CTypeSpecs::Bool,
1102 _ => {
1103 if let Some(rest) = s.strip_prefix("struct ") {
1105 if let Some(name) = interner.lookup(rest.trim()) {
1106 return CTypeSpecs::Struct { name: Some(name), is_union: false };
1107 }
1108 return CTypeSpecs::Struct { name: None, is_union: false };
1109 }
1110 if let Some(rest) = s.strip_prefix("union ") {
1111 if let Some(name) = interner.lookup(rest.trim()) {
1112 return CTypeSpecs::Struct { name: Some(name), is_union: true };
1113 }
1114 return CTypeSpecs::Struct { name: None, is_union: true };
1115 }
1116 if let Some(rest) = s.strip_prefix("enum ") {
1117 if let Some(name) = interner.lookup(rest.trim()) {
1118 return CTypeSpecs::Enum { name: Some(name) };
1119 }
1120 return CTypeSpecs::Enum { name: None };
1121 }
1122 if let Some(name) = interner.lookup(s) {
1124 CTypeSpecs::TypedefName(name)
1125 } else {
1126 CTypeSpecs::Void }
1130 }
1131 }
1132 }
1133
1134 pub fn to_display_string(&self, interner: &crate::intern::StringInterner) -> String {
1136 match self {
1137 TypeRepr::CType { specs, derived, .. } => {
1138 let base = specs.to_display_string(interner);
1139 let mut result = base;
1140 for d in derived {
1141 match d {
1142 CDerivedType::Pointer { is_const: true, .. } => result.push_str(" *const"),
1143 CDerivedType::Pointer { .. } => result.push_str(" *"),
1144 CDerivedType::Array { size: Some(n) } => {
1145 result.push_str(&format!("[{}]", n));
1146 }
1147 CDerivedType::Array { size: None } => result.push_str("[]"),
1148 CDerivedType::Function { .. } => result.push_str("()"),
1149 }
1150 }
1151 result
1152 }
1153 TypeRepr::RustType { repr, .. } => repr.to_display_string(),
1154 TypeRepr::Inferred(inferred) => inferred.to_display_string(interner),
1155 }
1156 }
1157
1158 pub fn to_rust_string(&self, interner: &crate::intern::StringInterner) -> String {
1160 match self {
1161 TypeRepr::CType { specs, derived, .. } => {
1162 let base = specs.to_rust_string(interner);
1163 let mut result = base;
1165 for d in derived.iter().rev() {
1166 if result == "()" && matches!(d, CDerivedType::Pointer { .. } | CDerivedType::Array { .. }) {
1168 result = "c_void".to_string();
1169 }
1170 result = match d {
1171 CDerivedType::Pointer { is_const: true, .. } => format!("*const {}", result),
1172 CDerivedType::Pointer { .. } => format!("*mut {}", result),
1173 CDerivedType::Array { size: Some(n) } => format!("[{}; {}]", result, n),
1174 CDerivedType::Array { size: None } => format!("*mut {}", result),
1175 CDerivedType::Function { .. } => format!("/* fn */"),
1176 };
1177 }
1178 result
1179 }
1180 TypeRepr::RustType { repr, .. } => repr.to_display_string(),
1181 TypeRepr::Inferred(inferred) => inferred.to_rust_string(interner),
1182 }
1183 }
1184}
1185
1186fn unified_to_c(
1193 ut: &crate::unified_type::UnifiedType,
1194 interner: &crate::intern::StringInterner,
1195) -> Option<(CTypeSpecs, Vec<CDerivedType>)> {
1196 use crate::unified_type::{UnifiedType as UT, IntSize as UIS};
1197
1198 match ut {
1199 UT::Void => Some((CTypeSpecs::Void, vec![])),
1200 UT::Bool => Some((CTypeSpecs::Bool, vec![])),
1201 UT::Char { signed } => Some((CTypeSpecs::Char { signed: *signed }, vec![])),
1202 UT::Int { signed, size } => {
1203 if matches!(size, UIS::Char) {
1206 return Some((CTypeSpecs::Char { signed: Some(*signed) }, vec![]));
1207 }
1208 let target = match size {
1209 UIS::Char => unreachable!(),
1210 UIS::Short => IntSize::Short,
1211 UIS::Int => IntSize::Int,
1212 UIS::Long => IntSize::Long,
1213 UIS::LongLong => IntSize::LongLong,
1214 UIS::Int128 => IntSize::Int128,
1215 };
1216 Some((CTypeSpecs::Int { signed: *signed, size: target }, vec![]))
1217 }
1218 UT::Float => Some((CTypeSpecs::Float, vec![])),
1219 UT::Double => Some((CTypeSpecs::Double { is_long: false }, vec![])),
1220 UT::LongDouble => Some((CTypeSpecs::Double { is_long: true }, vec![])),
1221 UT::Pointer { inner, is_const } => {
1222 let (specs, mut derived) = unified_to_c(inner, interner)?;
1223 derived.insert(
1225 0,
1226 CDerivedType::Pointer {
1227 is_const: *is_const,
1228 is_volatile: false,
1229 is_restrict: false,
1230 },
1231 );
1232 Some((specs, derived))
1233 }
1234 UT::Array { inner, size } => {
1235 let (specs, mut derived) = unified_to_c(inner, interner)?;
1236 derived.insert(0, CDerivedType::Array { size: *size });
1237 Some((specs, derived))
1238 }
1239 UT::Named(name) => {
1240 let specs = match interner.lookup(name) {
1241 Some(id) => CTypeSpecs::TypedefName(id),
1242 None => CTypeSpecs::UnknownTypedef(name.clone()),
1243 };
1244 Some((specs, vec![]))
1245 }
1246 UT::FnPtr { .. } | UT::Verbatim(_) | UT::Unknown => None,
1247 }
1248}
1249
1250impl TypeRepr {
1255 pub fn pointee_name(&self) -> Option<InternedStr> {
1260 match self {
1261 TypeRepr::CType { specs, derived, .. } => {
1262 if derived.iter().any(|d| matches!(d, CDerivedType::Pointer { .. })) {
1263 specs.type_name()
1264 } else {
1265 None
1266 }
1267 }
1268 TypeRepr::RustType { repr, .. } => repr.pointee_name(),
1269 TypeRepr::Inferred(inferred) => inferred.resolved_type()?.pointee_name(),
1270 }
1271 }
1272
1273 pub fn type_name(&self) -> Option<InternedStr> {
1278 match self {
1279 TypeRepr::CType { specs, .. } => specs.type_name(),
1280 TypeRepr::RustType { repr, .. } => repr.type_name(),
1281 TypeRepr::Inferred(inferred) => inferred.resolved_type()?.type_name(),
1282 }
1283 }
1284}
1285
1286impl CTypeSpecs {
1287 pub fn type_name(&self) -> Option<InternedStr> {
1289 match self {
1290 CTypeSpecs::Struct { name: Some(n), .. } => Some(*n),
1291 CTypeSpecs::TypedefName(n) => Some(*n),
1292 CTypeSpecs::Enum { name: Some(n) } => Some(*n),
1293 _ => None,
1294 }
1295 }
1296}
1297
1298impl InferredType {
1299 pub fn resolved_type(&self) -> Option<&TypeRepr> {
1304 match self {
1305 InferredType::Cast { target_type } => Some(target_type),
1306 InferredType::PtrMemberAccess { field_type: Some(ft), .. } => Some(ft),
1307 InferredType::MemberAccess { field_type: Some(ft), .. } => Some(ft),
1308 InferredType::ArraySubscript { element_type, .. } => Some(element_type),
1309 InferredType::AddressOf { inner_type } => Some(inner_type),
1310 InferredType::Dereference { pointer_type } => Some(pointer_type),
1311 InferredType::SymbolLookup { resolved_type, .. } => Some(resolved_type),
1312 InferredType::IncDec { inner_type } => Some(inner_type),
1313 InferredType::Assignment { lhs_type } => Some(lhs_type),
1314 InferredType::Comma { rhs_type } => Some(rhs_type),
1315 InferredType::Conditional { result_type, .. } => Some(result_type),
1316 InferredType::BinaryOp { result_type, .. } => Some(result_type),
1317 InferredType::UnaryArithmetic { inner_type } => Some(inner_type),
1318 InferredType::CompoundLiteral { type_name } => Some(type_name),
1319 InferredType::StmtExpr { last_expr_type } => last_expr_type.as_deref(),
1320 _ => None,
1321 }
1322 }
1323}
1324
1325impl RustTypeRepr {
1326 fn pointee_name(&self) -> Option<InternedStr> {
1331 None
1334 }
1335
1336 fn type_name(&self) -> Option<InternedStr> {
1338 None
1339 }
1340}
1341
1342impl CTypeSpecs {
1347 pub fn to_display_string(&self, interner: &crate::intern::StringInterner) -> String {
1349 match self {
1350 CTypeSpecs::Void => "void".to_string(),
1351 CTypeSpecs::Char { signed: None } => "char".to_string(),
1352 CTypeSpecs::Char { signed: Some(true) } => "signed char".to_string(),
1353 CTypeSpecs::Char { signed: Some(false) } => "unsigned char".to_string(),
1354 CTypeSpecs::Int { signed: true, size: IntSize::Short } => "short".to_string(),
1355 CTypeSpecs::Int { signed: false, size: IntSize::Short } => "unsigned short".to_string(),
1356 CTypeSpecs::Int { signed: true, size: IntSize::Int } => "int".to_string(),
1357 CTypeSpecs::Int { signed: false, size: IntSize::Int } => "unsigned int".to_string(),
1358 CTypeSpecs::Int { signed: true, size: IntSize::Long } => "long".to_string(),
1359 CTypeSpecs::Int { signed: false, size: IntSize::Long } => "unsigned long".to_string(),
1360 CTypeSpecs::Int { signed: true, size: IntSize::LongLong } => "long long".to_string(),
1361 CTypeSpecs::Int { signed: false, size: IntSize::LongLong } => "unsigned long long".to_string(),
1362 CTypeSpecs::Int { signed: true, size: IntSize::Int128 } => "__int128".to_string(),
1363 CTypeSpecs::Int { signed: false, size: IntSize::Int128 } => "unsigned __int128".to_string(),
1364 CTypeSpecs::Float => "float".to_string(),
1365 CTypeSpecs::Double { is_long: false } => "double".to_string(),
1366 CTypeSpecs::Double { is_long: true } => "long double".to_string(),
1367 CTypeSpecs::Bool => "_Bool".to_string(),
1368 CTypeSpecs::Struct { name: Some(n), is_union: false } => {
1369 format!("struct {}", interner.get(*n))
1370 }
1371 CTypeSpecs::Struct { name: None, is_union: false } => "struct".to_string(),
1372 CTypeSpecs::Struct { name: Some(n), is_union: true } => {
1373 format!("union {}", interner.get(*n))
1374 }
1375 CTypeSpecs::Struct { name: None, is_union: true } => "union".to_string(),
1376 CTypeSpecs::Enum { name: Some(n) } => format!("enum {}", interner.get(*n)),
1377 CTypeSpecs::Enum { name: None } => "enum".to_string(),
1378 CTypeSpecs::TypedefName(n) => interner.get(*n).to_string(),
1379 CTypeSpecs::UnknownTypedef(s) => s.clone(),
1380 }
1381 }
1382
1383 pub fn to_rust_string(&self, interner: &crate::intern::StringInterner) -> String {
1385 match self {
1386 CTypeSpecs::Void => "()".to_string(),
1387 CTypeSpecs::Char { signed: None } => "c_char".to_string(),
1388 CTypeSpecs::Char { signed: Some(true) } => "c_schar".to_string(),
1389 CTypeSpecs::Char { signed: Some(false) } => "c_uchar".to_string(),
1390 CTypeSpecs::Int { signed: true, size: IntSize::Short } => "c_short".to_string(),
1391 CTypeSpecs::Int { signed: false, size: IntSize::Short } => "c_ushort".to_string(),
1392 CTypeSpecs::Int { signed: true, size: IntSize::Int } => "c_int".to_string(),
1393 CTypeSpecs::Int { signed: false, size: IntSize::Int } => "c_uint".to_string(),
1394 CTypeSpecs::Int { signed: true, size: IntSize::Long } => "c_long".to_string(),
1395 CTypeSpecs::Int { signed: false, size: IntSize::Long } => "c_ulong".to_string(),
1396 CTypeSpecs::Int { signed: true, size: IntSize::LongLong } => "c_longlong".to_string(),
1397 CTypeSpecs::Int { signed: false, size: IntSize::LongLong } => "c_ulonglong".to_string(),
1398 CTypeSpecs::Int { signed: true, size: IntSize::Int128 } => "i128".to_string(),
1399 CTypeSpecs::Int { signed: false, size: IntSize::Int128 } => "u128".to_string(),
1400 CTypeSpecs::Float => "c_float".to_string(),
1401 CTypeSpecs::Double { is_long: false } => "c_double".to_string(),
1402 CTypeSpecs::Double { is_long: true } => "c_double".to_string(), CTypeSpecs::Bool => "bool".to_string(),
1404 CTypeSpecs::Struct { name: Some(n), .. } => interner.get(*n).to_string(),
1405 CTypeSpecs::Struct { name: None, .. } => "/* anonymous struct */".to_string(),
1406 CTypeSpecs::Enum { name: Some(n) } => interner.get(*n).to_string(),
1407 CTypeSpecs::Enum { name: None } => "/* anonymous enum */".to_string(),
1408 CTypeSpecs::TypedefName(n) => interner.get(*n).to_string(),
1409 CTypeSpecs::UnknownTypedef(s) => s.clone(),
1410 }
1411 }
1412}
1413
1414impl RustTypeRepr {
1415 pub fn make_outer_pointer_const(&mut self) {
1417 if let RustTypeRepr::Pointer { is_const, .. } = self {
1418 *is_const = true;
1419 }
1420 }
1421
1422 pub fn has_outer_pointer(&self) -> bool {
1424 matches!(self, RustTypeRepr::Pointer { .. })
1425 }
1426}
1427
1428impl RustTypeRepr {
1429 pub fn to_display_string(&self) -> String {
1431 match self {
1432 RustTypeRepr::CPrimitive(kind) => kind.to_string(),
1433 RustTypeRepr::RustPrimitive(kind) => kind.to_string(),
1434 RustTypeRepr::Pointer { inner, is_const: true } => {
1435 format!("*const {}", inner.to_display_string())
1436 }
1437 RustTypeRepr::Pointer { inner, is_const: false } => {
1438 format!("*mut {}", inner.to_display_string())
1439 }
1440 RustTypeRepr::Reference { inner, is_mut: true } => {
1441 format!("&mut {}", inner.to_display_string())
1442 }
1443 RustTypeRepr::Reference { inner, is_mut: false } => {
1444 format!("&{}", inner.to_display_string())
1445 }
1446 RustTypeRepr::Named(name) => name.clone(),
1447 RustTypeRepr::Option(inner) => format!("Option<{}>", inner.to_display_string()),
1448 RustTypeRepr::FnPointer { params, ret } => {
1449 let params_str: Vec<_> = params.iter().map(|p| p.to_display_string()).collect();
1450 let ret_str = ret
1451 .as_ref()
1452 .map(|r| format!(" -> {}", r.to_display_string()))
1453 .unwrap_or_default();
1454 format!("fn({}){}", params_str.join(", "), ret_str)
1455 }
1456 RustTypeRepr::Unit => "()".to_string(),
1457 RustTypeRepr::Unknown(s) => s.clone(),
1458 }
1459 }
1460}
1461
1462impl fmt::Display for CPrimitiveKind {
1463 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1464 let s = match self {
1465 CPrimitiveKind::CChar => "c_char",
1466 CPrimitiveKind::CSchar => "c_schar",
1467 CPrimitiveKind::CUchar => "c_uchar",
1468 CPrimitiveKind::CShort => "c_short",
1469 CPrimitiveKind::CUshort => "c_ushort",
1470 CPrimitiveKind::CInt => "c_int",
1471 CPrimitiveKind::CUint => "c_uint",
1472 CPrimitiveKind::CLong => "c_long",
1473 CPrimitiveKind::CUlong => "c_ulong",
1474 CPrimitiveKind::CLongLong => "c_longlong",
1475 CPrimitiveKind::CUlongLong => "c_ulonglong",
1476 CPrimitiveKind::CFloat => "c_float",
1477 CPrimitiveKind::CDouble => "c_double",
1478 };
1479 write!(f, "{}", s)
1480 }
1481}
1482
1483impl fmt::Display for RustPrimitiveKind {
1484 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1485 let s = match self {
1486 RustPrimitiveKind::I8 => "i8",
1487 RustPrimitiveKind::I16 => "i16",
1488 RustPrimitiveKind::I32 => "i32",
1489 RustPrimitiveKind::I64 => "i64",
1490 RustPrimitiveKind::I128 => "i128",
1491 RustPrimitiveKind::Isize => "isize",
1492 RustPrimitiveKind::U8 => "u8",
1493 RustPrimitiveKind::U16 => "u16",
1494 RustPrimitiveKind::U32 => "u32",
1495 RustPrimitiveKind::U64 => "u64",
1496 RustPrimitiveKind::U128 => "u128",
1497 RustPrimitiveKind::Usize => "usize",
1498 RustPrimitiveKind::F32 => "f32",
1499 RustPrimitiveKind::F64 => "f64",
1500 RustPrimitiveKind::Bool => "bool",
1501 };
1502 write!(f, "{}", s)
1503 }
1504}
1505
1506impl InferredType {
1507 pub fn to_display_string(&self, interner: &crate::intern::StringInterner) -> String {
1509 match self {
1510 InferredType::IntLiteral => "int".to_string(),
1511 InferredType::UIntLiteral => "unsigned int".to_string(),
1512 InferredType::FloatLiteral => "double".to_string(),
1513 InferredType::CharLiteral => "int".to_string(),
1514 InferredType::StringLiteral => "char *".to_string(),
1515 InferredType::SymbolLookup { resolved_type, .. } => {
1516 resolved_type.to_display_string(interner)
1517 }
1518 InferredType::ThxDefault => "*mut PerlInterpreter".to_string(),
1519 InferredType::BinaryOp { result_type, .. } => result_type.to_display_string(interner),
1520 InferredType::UnaryArithmetic { inner_type } => inner_type.to_display_string(interner),
1521 InferredType::LogicalNot => "int".to_string(),
1522 InferredType::AddressOf { inner_type } => {
1523 format!("{} *", inner_type.to_display_string(interner))
1524 }
1525 InferredType::Dereference { pointer_type } => {
1526 let s = pointer_type.to_display_string(interner);
1527 s.trim_end_matches(" *").to_string()
1528 }
1529 InferredType::IncDec { inner_type } => inner_type.to_display_string(interner),
1530 InferredType::MemberAccess { field_type: Some(ft), .. } => {
1531 ft.to_display_string(interner)
1532 }
1533 InferredType::MemberAccess { base_type, member, .. } => {
1534 format!("{}.{}", base_type, interner.get(*member))
1535 }
1536 InferredType::PtrMemberAccess { field_type: Some(ft), .. } => {
1537 ft.to_display_string(interner)
1538 }
1539 InferredType::PtrMemberAccess { base_type, member, .. } => {
1540 format!("{}->{}", base_type, interner.get(*member))
1541 }
1542 InferredType::ArraySubscript { element_type, .. } => {
1543 element_type.to_display_string(interner)
1544 }
1545 InferredType::Conditional { result_type, .. } => {
1546 result_type.to_display_string(interner)
1547 }
1548 InferredType::Comma { rhs_type } => rhs_type.to_display_string(interner),
1549 InferredType::Assignment { lhs_type } => lhs_type.to_display_string(interner),
1550 InferredType::Cast { target_type } => target_type.to_display_string(interner),
1551 InferredType::Sizeof | InferredType::Alignof => "unsigned long".to_string(),
1552 InferredType::CompoundLiteral { type_name } => type_name.to_display_string(interner),
1553 InferredType::StmtExpr { last_expr_type: Some(t) } => t.to_display_string(interner),
1554 InferredType::StmtExpr { last_expr_type: None } => "void".to_string(),
1555 InferredType::Assert => "void".to_string(),
1556 InferredType::FunctionReturn { func_name } => {
1557 format!("{}()", interner.get(*func_name))
1558 }
1559 }
1560 }
1561
1562 pub fn to_rust_string(&self, interner: &crate::intern::StringInterner) -> String {
1564 match self {
1565 InferredType::IntLiteral => "c_int".to_string(),
1566 InferredType::UIntLiteral => "c_uint".to_string(),
1567 InferredType::FloatLiteral => "c_double".to_string(),
1568 InferredType::CharLiteral => "c_int".to_string(),
1569 InferredType::StringLiteral => "*const c_char".to_string(),
1570 InferredType::SymbolLookup { resolved_type, .. } => {
1571 resolved_type.to_rust_string(interner)
1572 }
1573 InferredType::ThxDefault => "*mut PerlInterpreter".to_string(),
1574 InferredType::BinaryOp { result_type, .. } => result_type.to_rust_string(interner),
1575 InferredType::UnaryArithmetic { inner_type } => inner_type.to_rust_string(interner),
1576 InferredType::LogicalNot => "c_int".to_string(),
1577 InferredType::AddressOf { inner_type } => {
1578 format!("*mut {}", inner_type.to_rust_string(interner))
1579 }
1580 InferredType::Dereference { pointer_type } => {
1581 let s = pointer_type.to_rust_string(interner);
1582 s.strip_prefix("*mut ").or_else(|| s.strip_prefix("*const "))
1584 .unwrap_or(&s).to_string()
1585 }
1586 InferredType::IncDec { inner_type } => inner_type.to_rust_string(interner),
1587 InferredType::MemberAccess { field_type: Some(ft), .. } => {
1588 ft.to_rust_string(interner)
1589 }
1590 InferredType::MemberAccess { base_type, member, .. } => {
1591 format!("/* {}.{} */", base_type, interner.get(*member))
1592 }
1593 InferredType::PtrMemberAccess { field_type: Some(ft), .. } => {
1594 ft.to_rust_string(interner)
1595 }
1596 InferredType::PtrMemberAccess { base_type, member, .. } => {
1597 format!("/* {}->{} */", base_type, interner.get(*member))
1598 }
1599 InferredType::ArraySubscript { element_type, .. } => {
1600 element_type.to_rust_string(interner)
1601 }
1602 InferredType::Conditional { result_type, .. } => {
1603 result_type.to_rust_string(interner)
1604 }
1605 InferredType::Comma { rhs_type } => rhs_type.to_rust_string(interner),
1606 InferredType::Assignment { lhs_type } => lhs_type.to_rust_string(interner),
1607 InferredType::Cast { target_type } => target_type.to_rust_string(interner),
1608 InferredType::Sizeof | InferredType::Alignof => "c_ulong".to_string(),
1609 InferredType::CompoundLiteral { type_name } => type_name.to_rust_string(interner),
1610 InferredType::StmtExpr { last_expr_type: Some(t) } => t.to_rust_string(interner),
1611 InferredType::StmtExpr { last_expr_type: None } => "()".to_string(),
1612 InferredType::Assert => "()".to_string(),
1613 InferredType::FunctionReturn { func_name } => {
1614 format!("/* {}() ret */", interner.get(*func_name))
1615 }
1616 }
1617 }
1618}
1619
1620#[cfg(test)]
1625mod tests {
1626 use super::*;
1627
1628 #[test]
1629 fn test_rust_type_repr_from_string() {
1630 assert!(matches!(
1631 RustTypeRepr::from_type_string("c_int"),
1632 RustTypeRepr::CPrimitive(CPrimitiveKind::CInt)
1633 ));
1634
1635 assert!(matches!(
1636 RustTypeRepr::from_type_string("i32"),
1637 RustTypeRepr::RustPrimitive(RustPrimitiveKind::I32)
1638 ));
1639
1640 assert!(matches!(
1641 RustTypeRepr::from_type_string("()"),
1642 RustTypeRepr::Unit
1643 ));
1644
1645 if let RustTypeRepr::Pointer { inner, is_const: false } =
1646 RustTypeRepr::from_type_string("*mut SV")
1647 {
1648 assert!(matches!(*inner, RustTypeRepr::Named(ref n) if n == "SV"));
1649 } else {
1650 panic!("Expected *mut SV");
1651 }
1652
1653 if let RustTypeRepr::Pointer { inner, is_const: true } =
1654 RustTypeRepr::from_type_string("*const c_char")
1655 {
1656 assert!(matches!(*inner, RustTypeRepr::CPrimitive(CPrimitiveKind::CChar)));
1657 } else {
1658 panic!("Expected *const c_char");
1659 }
1660 }
1661
1662 #[test]
1663 fn test_rust_type_repr_from_string_with_spaces() {
1664 if let RustTypeRepr::Pointer { inner, is_const: false } =
1666 RustTypeRepr::from_type_string("* mut SV")
1667 {
1668 assert!(matches!(*inner, RustTypeRepr::Named(ref n) if n == "SV"));
1669 } else {
1670 panic!("Expected * mut SV");
1671 }
1672 }
1673
1674 #[test]
1675 fn test_c_primitive_display() {
1676 assert_eq!(CPrimitiveKind::CInt.to_string(), "c_int");
1677 assert_eq!(CPrimitiveKind::CUlong.to_string(), "c_ulong");
1678 }
1679
1680 #[test]
1681 fn test_rust_primitive_display() {
1682 assert_eq!(RustPrimitiveKind::I32.to_string(), "i32");
1683 assert_eq!(RustPrimitiveKind::Usize.to_string(), "usize");
1684 }
1685
1686 fn make_void_ptr() -> TypeRepr {
1689 TypeRepr::CType {
1690 specs: CTypeSpecs::Void,
1691 derived: vec![CDerivedType::Pointer {
1692 is_const: false,
1693 is_volatile: false,
1694 is_restrict: false,
1695 }],
1696 source: CTypeSource::Apidoc { raw: "void *".to_string() },
1697 }
1698 }
1699
1700 fn make_concrete_ptr() -> TypeRepr {
1701 TypeRepr::CType {
1702 specs: CTypeSpecs::Char { signed: None },
1703 derived: vec![CDerivedType::Pointer {
1704 is_const: false,
1705 is_volatile: false,
1706 is_restrict: false,
1707 }],
1708 source: CTypeSource::Apidoc { raw: "char *".to_string() },
1709 }
1710 }
1711
1712 #[test]
1713 fn test_void_pointer_structural() {
1714 let vp = make_void_ptr();
1715 assert!(vp.is_pointer_type());
1716 assert!(vp.is_void_pointer());
1717 assert!(!vp.is_concrete_pointer());
1718 }
1719
1720 #[test]
1721 fn test_concrete_pointer_structural() {
1722 let cp = make_concrete_ptr();
1723 assert!(cp.is_pointer_type());
1724 assert!(!cp.is_void_pointer());
1725 assert!(cp.is_concrete_pointer());
1726 }
1727
1728 #[test]
1729 fn test_inferred_member_access_pointer_recursive() {
1730 let inner = make_concrete_ptr();
1734 let inferred = TypeRepr::Inferred(InferredType::MemberAccess {
1735 base_type: "xpvcv".to_string(),
1736 member: crate::intern::StringInterner::new().intern("foo"),
1737 field_type: Some(Box::new(inner)),
1738 });
1739 assert!(inferred.is_pointer_type());
1740 assert!(!inferred.is_void_pointer());
1741 assert!(inferred.is_concrete_pointer());
1742 assert!(!inferred.has_outer_pointer());
1744 }
1745}