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 PatchOverride { raw: String },
56 InlineFn { func_name: InternedStr },
58 Parser,
60 FieldInference { field_name: InternedStr },
62 Cast,
64 SvFamilyCast,
66 CommonMacroFieldInference,
71}
72
73#[derive(Debug, Clone)]
79pub enum RustTypeSource {
80 FnParam { func_name: String, param_index: usize },
82 FnReturn { func_name: String },
84 Const { const_name: String },
86 Parsed { raw: String },
88 Propagated { raw: String },
91}
92
93#[derive(Debug, Clone, PartialEq, Eq)]
99pub enum CTypeSpecs {
100 Void,
102 Char { signed: Option<bool> },
104 Int { signed: bool, size: IntSize },
106 Float,
108 Double { is_long: bool },
110 Bool,
112 Struct { name: Option<InternedStr>, is_union: bool },
114 Enum { name: Option<InternedStr> },
116 TypedefName(InternedStr),
118 UnknownTypedef(String),
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq)]
124pub enum IntSize {
125 Short,
127 Int,
129 Long,
131 LongLong,
133 Int128,
135}
136
137#[derive(Debug, Clone, PartialEq, Eq)]
139pub enum CDerivedType {
140 Pointer {
142 is_const: bool,
143 is_volatile: bool,
144 is_restrict: bool,
145 },
146 Array { size: Option<usize> },
148 Function {
150 params: Vec<CTypeSpecs>,
151 variadic: bool,
152 },
153}
154
155#[derive(Debug, Clone, PartialEq, Eq)]
161pub enum RustTypeRepr {
162 CPrimitive(CPrimitiveKind),
164 RustPrimitive(RustPrimitiveKind),
166 Pointer {
168 inner: Box<RustTypeRepr>,
169 is_const: bool,
170 },
171 Reference {
173 inner: Box<RustTypeRepr>,
174 is_mut: bool,
175 },
176 Named(String),
178 Option(Box<RustTypeRepr>),
180 FnPointer {
182 params: Vec<RustTypeRepr>,
183 ret: Option<Box<RustTypeRepr>>,
184 },
185 Unit,
187 Unknown(String),
189}
190
191#[derive(Debug, Clone, Copy, PartialEq, Eq)]
193pub enum CPrimitiveKind {
194 CChar,
195 CSchar,
196 CUchar,
197 CShort,
198 CUshort,
199 CInt,
200 CUint,
201 CLong,
202 CUlong,
203 CLongLong,
204 CUlongLong,
205 CFloat,
206 CDouble,
207}
208
209#[derive(Debug, Clone, Copy, PartialEq, Eq)]
211pub enum RustPrimitiveKind {
212 I8,
213 I16,
214 I32,
215 I64,
216 I128,
217 Isize,
218 U8,
219 U16,
220 U32,
221 U64,
222 U128,
223 Usize,
224 F32,
225 F64,
226 Bool,
227}
228
229#[derive(Debug, Clone)]
235pub enum InferredType {
236 IntLiteral,
239 UIntLiteral,
241 FloatLiteral,
243 CharLiteral,
245 StringLiteral,
247
248 SymbolLookup {
251 name: InternedStr,
252 resolved_type: Box<TypeRepr>,
254 },
255 ThxDefault,
257
258 BinaryOp {
261 op: BinOp,
262 result_type: Box<TypeRepr>,
264 },
265 UnaryArithmetic {
267 inner_type: Box<TypeRepr>,
269 },
270 LogicalNot,
272 AddressOf { inner_type: Box<TypeRepr> },
274 Dereference { pointer_type: Box<TypeRepr> },
276 IncDec { inner_type: Box<TypeRepr> },
278
279 MemberAccess {
282 base_type: String,
283 member: InternedStr,
284 field_type: Option<Box<TypeRepr>>,
286 },
287 PtrMemberAccess {
289 base_type: String,
290 member: InternedStr,
291 field_type: Option<Box<TypeRepr>>,
293 used_consistent_type: bool,
295 },
296
297 ArraySubscript {
300 base_type: Box<TypeRepr>,
301 element_type: Box<TypeRepr>,
303 },
304
305 Conditional {
308 then_type: Box<TypeRepr>,
309 else_type: Box<TypeRepr>,
310 result_type: Box<TypeRepr>,
312 },
313 Comma {
315 rhs_type: Box<TypeRepr>,
317 },
318 Assignment {
320 lhs_type: Box<TypeRepr>,
322 },
323
324 Cast { target_type: Box<TypeRepr> },
327 Sizeof,
329 Alignof,
331 CompoundLiteral { type_name: Box<TypeRepr> },
333
334 StmtExpr {
337 last_expr_type: Option<Box<TypeRepr>>,
339 },
340 Assert,
342 FunctionReturn { func_name: InternedStr },
344}
345
346impl CTypeSpecs {
351 pub fn from_decl_specs(specs: &crate::ast::DeclSpecs, _interner: &crate::intern::StringInterner) -> Self {
353 use crate::ast::TypeSpec;
354
355 let mut has_signed = false;
356 let mut has_unsigned = false;
357 let mut has_short = false;
358 let mut has_long: u8 = 0;
359 let mut base_type: Option<CTypeSpecs> = None;
360
361 for type_spec in &specs.type_specs {
362 match type_spec {
363 TypeSpec::Void => base_type = Some(CTypeSpecs::Void),
364 TypeSpec::Char => {
365 if base_type.is_none() {
367 base_type = Some(CTypeSpecs::Char { signed: None });
368 }
369 }
370 TypeSpec::Short => has_short = true,
371 TypeSpec::Int => {
372 if base_type.is_none() {
373 base_type = Some(CTypeSpecs::Int {
374 signed: true,
375 size: IntSize::Int,
376 });
377 }
378 }
379 TypeSpec::Long => has_long += 1,
380 TypeSpec::Float => base_type = Some(CTypeSpecs::Float),
381 TypeSpec::Double => base_type = Some(CTypeSpecs::Double { is_long: false }),
382 TypeSpec::Signed => has_signed = true,
383 TypeSpec::Unsigned => has_unsigned = true,
384 TypeSpec::Bool => base_type = Some(CTypeSpecs::Bool),
385 TypeSpec::Int128 => {
386 base_type = Some(CTypeSpecs::Int {
387 signed: !has_unsigned,
388 size: IntSize::Int128,
389 });
390 }
391 TypeSpec::Struct(s) => {
392 base_type = Some(CTypeSpecs::Struct {
393 name: s.name,
394 is_union: false,
395 });
396 }
397 TypeSpec::Union(s) => {
398 base_type = Some(CTypeSpecs::Struct {
399 name: s.name,
400 is_union: true,
401 });
402 }
403 TypeSpec::Enum(e) => {
404 base_type = Some(CTypeSpecs::Enum { name: e.name });
405 }
406 TypeSpec::TypedefName(name) => {
407 base_type = Some(CTypeSpecs::TypedefName(*name));
408 }
409 _ => {}
410 }
411 }
412
413 if has_short {
415 return CTypeSpecs::Int {
416 signed: !has_unsigned,
417 size: IntSize::Short,
418 };
419 }
420
421 if has_long >= 2 {
422 return CTypeSpecs::Int {
423 signed: !has_unsigned,
424 size: IntSize::LongLong,
425 };
426 }
427
428 if has_long == 1 {
429 if let Some(CTypeSpecs::Double { .. }) = base_type {
430 return CTypeSpecs::Double { is_long: true };
431 }
432 return CTypeSpecs::Int {
433 signed: !has_unsigned,
434 size: IntSize::Long,
435 };
436 }
437
438 if let Some(CTypeSpecs::Char { .. }) = base_type {
440 if has_signed {
441 return CTypeSpecs::Char { signed: Some(true) };
442 } else if has_unsigned {
443 return CTypeSpecs::Char { signed: Some(false) };
444 }
445 return CTypeSpecs::Char { signed: None };
446 }
447
448 if has_unsigned && base_type.is_none() {
450 return CTypeSpecs::Int {
451 signed: false,
452 size: IntSize::Int,
453 };
454 }
455 if has_signed && base_type.is_none() {
456 return CTypeSpecs::Int {
457 signed: true,
458 size: IntSize::Int,
459 };
460 }
461
462 if has_unsigned {
464 if let Some(CTypeSpecs::Int { size, .. }) = base_type {
465 return CTypeSpecs::Int {
466 signed: false,
467 size,
468 };
469 }
470 }
471
472 base_type.unwrap_or(CTypeSpecs::Int {
473 signed: true,
474 size: IntSize::Int,
475 })
476 }
477}
478
479pub fn shift_pointee_const(derived: &mut [CDerivedType], base_is_const: bool) {
492 let ptr_idxs: Vec<usize> = derived
493 .iter()
494 .enumerate()
495 .filter(|(_, d)| matches!(d, CDerivedType::Pointer { .. }))
496 .map(|(i, _)| i)
497 .collect();
498 if ptr_idxs.is_empty() {
499 return;
500 }
501 let old_quals: Vec<bool> = ptr_idxs
502 .iter()
503 .map(|&i| match &derived[i] {
504 CDerivedType::Pointer { is_const, .. } => *is_const,
505 _ => unreachable!(),
506 })
507 .collect();
508 for (j, &i) in ptr_idxs.iter().enumerate() {
509 let new_const = if j + 1 == ptr_idxs.len() {
511 base_is_const
512 } else {
513 old_quals[j + 1]
514 };
515 if let CDerivedType::Pointer { is_const, .. } = &mut derived[i] {
516 *is_const = new_const;
517 }
518 }
519}
520
521impl CDerivedType {
522 pub fn from_derived_decls_with_base_const(
526 derived: &[crate::ast::DerivedDecl],
527 base_is_const: bool,
528 ) -> Vec<Self> {
529 let mut v = Self::from_derived_decls(derived);
530 shift_pointee_const(&mut v, base_is_const);
531 v
532 }
533
534 pub fn from_derived_decls(derived: &[crate::ast::DerivedDecl]) -> Vec<Self> {
536 use crate::ast::ExprKind;
537
538 derived
539 .iter()
540 .map(|d| match d {
541 crate::ast::DerivedDecl::Pointer(quals) => CDerivedType::Pointer {
542 is_const: quals.is_const,
543 is_volatile: quals.is_volatile,
544 is_restrict: quals.is_restrict,
545 },
546 crate::ast::DerivedDecl::Array(array_decl) => {
547 let size = array_decl.size.as_ref().and_then(|expr| {
549 match &expr.kind {
550 ExprKind::IntLit(n) => Some(*n as usize),
551 ExprKind::UIntLit(n) => Some(*n as usize),
552 _ => None,
553 }
554 });
555 CDerivedType::Array { size }
556 }
557 crate::ast::DerivedDecl::Function(_params) => {
558 CDerivedType::Function {
560 params: vec![],
561 variadic: false,
562 }
563 }
564 })
565 .collect()
566 }
567}
568
569impl RustTypeRepr {
570 pub fn from_type_string(s: &str) -> Self {
572 let s = s.trim();
573
574 if s == "()" {
576 return RustTypeRepr::Unit;
577 }
578
579 if let Some(rest) = s.strip_prefix("*mut ") {
581 return RustTypeRepr::Pointer {
582 inner: Box::new(Self::from_type_string(rest)),
583 is_const: false,
584 };
585 }
586 if let Some(rest) = s.strip_prefix("* mut ") {
587 return RustTypeRepr::Pointer {
588 inner: Box::new(Self::from_type_string(rest)),
589 is_const: false,
590 };
591 }
592 if let Some(rest) = s.strip_prefix("*const ") {
593 return RustTypeRepr::Pointer {
594 inner: Box::new(Self::from_type_string(rest)),
595 is_const: true,
596 };
597 }
598 if let Some(rest) = s.strip_prefix("* const ") {
599 return RustTypeRepr::Pointer {
600 inner: Box::new(Self::from_type_string(rest)),
601 is_const: true,
602 };
603 }
604
605 if let Some(rest) = s.strip_prefix("&mut ") {
607 return RustTypeRepr::Reference {
608 inner: Box::new(Self::from_type_string(rest)),
609 is_mut: true,
610 };
611 }
612 if let Some(rest) = s.strip_prefix("& mut ") {
613 return RustTypeRepr::Reference {
614 inner: Box::new(Self::from_type_string(rest)),
615 is_mut: true,
616 };
617 }
618 if let Some(rest) = s.strip_prefix('&') {
619 return RustTypeRepr::Reference {
620 inner: Box::new(Self::from_type_string(rest.trim())),
621 is_mut: false,
622 };
623 }
624
625 if let Some(kind) = Self::parse_c_primitive(s) {
627 return RustTypeRepr::CPrimitive(kind);
628 }
629
630 if let Some(kind) = Self::parse_rust_primitive(s) {
632 return RustTypeRepr::RustPrimitive(kind);
633 }
634
635 if s.starts_with("Option<") || s.starts_with(":: std :: option :: Option<") {
637 if let Some(inner) = Self::extract_generic_param(s, "Option") {
638 return RustTypeRepr::Option(Box::new(Self::from_type_string(&inner)));
639 }
640 }
641
642 if s.chars().next().map(|c| c.is_alphabetic() || c == '_').unwrap_or(false) {
644 let name = s.split("::").last().unwrap_or(s).trim();
646 return RustTypeRepr::Named(name.to_string());
647 }
648
649 RustTypeRepr::Unknown(s.to_string())
651 }
652
653 fn parse_c_primitive(s: &str) -> Option<CPrimitiveKind> {
655 let s = s.trim();
657 let name = if s.contains("::") {
658 s.split("::").last()?.trim()
659 } else {
660 s
661 };
662
663 match name {
664 "c_char" => Some(CPrimitiveKind::CChar),
665 "c_schar" => Some(CPrimitiveKind::CSchar),
666 "c_uchar" => Some(CPrimitiveKind::CUchar),
667 "c_short" => Some(CPrimitiveKind::CShort),
668 "c_ushort" => Some(CPrimitiveKind::CUshort),
669 "c_int" => Some(CPrimitiveKind::CInt),
670 "c_uint" => Some(CPrimitiveKind::CUint),
671 "c_long" => Some(CPrimitiveKind::CLong),
672 "c_ulong" => Some(CPrimitiveKind::CUlong),
673 "c_longlong" => Some(CPrimitiveKind::CLongLong),
674 "c_ulonglong" => Some(CPrimitiveKind::CUlongLong),
675 "c_float" => Some(CPrimitiveKind::CFloat),
676 "c_double" => Some(CPrimitiveKind::CDouble),
677 _ => None,
678 }
679 }
680
681 fn parse_rust_primitive(s: &str) -> Option<RustPrimitiveKind> {
683 match s.trim() {
684 "i8" => Some(RustPrimitiveKind::I8),
685 "i16" => Some(RustPrimitiveKind::I16),
686 "i32" => Some(RustPrimitiveKind::I32),
687 "i64" => Some(RustPrimitiveKind::I64),
688 "i128" => Some(RustPrimitiveKind::I128),
689 "isize" => Some(RustPrimitiveKind::Isize),
690 "u8" => Some(RustPrimitiveKind::U8),
691 "u16" => Some(RustPrimitiveKind::U16),
692 "u32" => Some(RustPrimitiveKind::U32),
693 "u64" => Some(RustPrimitiveKind::U64),
694 "u128" => Some(RustPrimitiveKind::U128),
695 "usize" => Some(RustPrimitiveKind::Usize),
696 "f32" => Some(RustPrimitiveKind::F32),
697 "f64" => Some(RustPrimitiveKind::F64),
698 "bool" => Some(RustPrimitiveKind::Bool),
699 _ => None,
700 }
701 }
702
703 fn extract_generic_param(s: &str, type_name: &str) -> Option<String> {
705 let start = s.find(&format!("{}<", type_name))?;
707 let after_open = start + type_name.len() + 1;
708 let content = &s[after_open..];
709
710 let mut depth = 1;
712 let mut end = 0;
713 for (i, c) in content.char_indices() {
714 match c {
715 '<' => depth += 1,
716 '>' => {
717 depth -= 1;
718 if depth == 0 {
719 end = i;
720 break;
721 }
722 }
723 _ => {}
724 }
725 }
726
727 if end > 0 {
728 Some(content[..end].trim().to_string())
729 } else {
730 None
731 }
732 }
733}
734
735impl TypeRepr {
736 pub fn source_display(&self) -> &'static str {
738 match self {
739 TypeRepr::CType { source, .. } => match source {
740 CTypeSource::Header => "c-header",
741 CTypeSource::Apidoc { .. } => "apidoc",
742 CTypeSource::PatchOverride { .. } => "patch-override",
743 CTypeSource::InlineFn { .. } => "inline-fn",
744 CTypeSource::Parser => "parser",
745 CTypeSource::FieldInference { .. } => "field-inference",
746 CTypeSource::Cast => "cast",
747 CTypeSource::SvFamilyCast => "sv-family-cast",
748 CTypeSource::CommonMacroFieldInference => "common-macro-field-inference",
749 },
750 TypeRepr::RustType { .. } => "rust-bindings",
751 TypeRepr::Inferred(_) => "inferred",
752 }
753 }
754
755 pub fn is_fn_param_source(&self) -> bool {
761 matches!(self, TypeRepr::RustType { source: RustTypeSource::FnParam { .. }, .. })
762 }
763
764 pub fn confidence_tier(&self) -> u8 {
771 match self {
772 TypeRepr::RustType { source, .. } => match source {
773 RustTypeSource::FnParam { .. }
774 | RustTypeSource::FnReturn { .. }
775 | RustTypeSource::Const { .. } => 1,
776 RustTypeSource::Parsed { .. } => 3,
777 RustTypeSource::Propagated { .. } => 4,
778 },
779 TypeRepr::CType { source, .. } => match source {
780 CTypeSource::PatchOverride { .. } => 0,
781 CTypeSource::InlineFn { .. } | CTypeSource::Header => 2,
782 CTypeSource::Apidoc { .. }
783 | CTypeSource::CommonMacroFieldInference => 3,
784 CTypeSource::Cast
785 | CTypeSource::SvFamilyCast
786 | CTypeSource::FieldInference { .. }
787 | CTypeSource::Parser => 4,
788 },
789 TypeRepr::Inferred(_) => 4,
790 }
791 }
792
793 pub fn is_void(&self) -> bool {
794 match self {
795 TypeRepr::CType { specs, derived, .. } => {
796 derived.is_empty() && matches!(specs, CTypeSpecs::Void)
798 }
799 TypeRepr::RustType { repr, .. } => {
800 matches!(repr, RustTypeRepr::Unit)
801 }
802 TypeRepr::Inferred(inferred) => {
803 match inferred {
804 InferredType::SymbolLookup { resolved_type, .. } => {
805 resolved_type.is_void()
806 }
807 _ => false,
808 }
809 }
810 }
811 }
812
813 pub fn make_outer_pointer_mut(&mut self) {
816 match self {
817 TypeRepr::CType { derived, .. } => {
818 for d in derived.iter_mut().rev() {
819 if let CDerivedType::Pointer { is_const, .. } = d {
820 *is_const = false;
821 return;
822 }
823 }
824 }
825 TypeRepr::RustType { repr, .. } => {
826 if let RustTypeRepr::Pointer { is_const, .. } = repr {
827 *is_const = false;
828 }
829 }
830 _ => {}
831 }
832 }
833
834 pub fn make_outer_pointer_const(&mut self) {
835 match self {
836 TypeRepr::CType { derived, .. } => {
837 for d in derived.iter_mut().rev() {
839 if let CDerivedType::Pointer { is_const, .. } = d {
840 *is_const = true;
841 return;
842 }
843 }
844 }
845 TypeRepr::RustType { repr, .. } => {
846 repr.make_outer_pointer_const();
847 }
848 TypeRepr::Inferred(inferred) => {
849 match inferred {
850 InferredType::SymbolLookup { resolved_type, .. } => {
851 resolved_type.make_outer_pointer_const();
852 }
853 InferredType::Cast { target_type } => {
854 target_type.make_outer_pointer_const();
855 }
856 _ => {}
857 }
858 }
859 }
860 }
861
862 pub fn is_pointer_type(&self) -> bool {
869 match self {
870 TypeRepr::CType { derived, .. } => {
871 derived.iter().any(|d| matches!(d, CDerivedType::Pointer { .. }))
872 }
873 TypeRepr::RustType { repr, .. } => repr.has_outer_pointer(),
874 TypeRepr::Inferred(inferred) => inferred
875 .resolved_type()
876 .is_some_and(|t| t.is_pointer_type()),
877 }
878 }
879
880 pub fn is_void_pointer(&self) -> bool {
886 match self {
887 TypeRepr::CType { specs, derived, .. } => {
888 derived.iter().any(|d| matches!(d, CDerivedType::Pointer { .. }))
889 && matches!(specs, CTypeSpecs::Void)
890 }
891 TypeRepr::RustType { repr, .. } => match repr {
892 RustTypeRepr::Pointer { inner, .. } => {
893 matches!(inner.as_ref(), RustTypeRepr::Unit)
894 || matches!(inner.as_ref(), RustTypeRepr::Named(n) if n == "c_void")
895 }
896 _ => false,
897 },
898 TypeRepr::Inferred(inferred) => inferred
899 .resolved_type()
900 .is_some_and(|t| t.is_void_pointer()),
901 }
902 }
903
904 pub fn is_concrete_pointer(&self) -> bool {
906 self.is_pointer_type() && !self.is_void_pointer()
907 }
908
909 pub fn has_outer_pointer(&self) -> bool {
914 match self {
915 TypeRepr::CType { derived, .. } => {
916 derived.iter().any(|d| matches!(d, CDerivedType::Pointer { .. }))
917 }
918 TypeRepr::RustType { repr, .. } => repr.has_outer_pointer(),
919 _ => false,
920 }
921 }
922
923 pub fn from_apidoc_string(s: &str, interner: &crate::intern::StringInterner) -> Self {
925 let (specs, derived) = Self::parse_c_type_string(s, interner);
927 TypeRepr::CType {
928 specs,
929 derived,
930 source: CTypeSource::Apidoc { raw: s.to_string() },
931 }
932 }
933
934 pub fn from_rust_string(s: &str) -> Self {
939 let repr = RustTypeRepr::from_type_string(s);
940 TypeRepr::RustType {
941 repr,
942 source: RustTypeSource::Parsed {
943 raw: s.to_string(),
944 },
945 }
946 }
947
948 pub fn from_rust_string_propagated(s: &str) -> Self {
953 let repr = RustTypeRepr::from_type_string(s);
954 TypeRepr::RustType {
955 repr,
956 source: RustTypeSource::Propagated {
957 raw: s.to_string(),
958 },
959 }
960 }
961
962 pub fn from_unified_type(
975 ut: &crate::unified_type::UnifiedType,
976 interner: &crate::intern::StringInterner,
977 ) -> Self {
978 let raw = ut.to_rust_string();
979 if let Some((specs, derived)) = unified_to_c(ut, interner) {
980 return TypeRepr::CType {
981 specs,
982 derived,
983 source: CTypeSource::Apidoc { raw },
984 };
985 }
986 TypeRepr::RustType {
987 repr: RustTypeRepr::Unknown(raw.clone()),
988 source: RustTypeSource::Parsed { raw },
989 }
990 }
991
992 pub fn from_decl(
997 specs: &crate::ast::DeclSpecs,
998 declarator: &crate::ast::Declarator,
999 _interner: &crate::intern::StringInterner,
1000 ) -> Self {
1001 let c_specs = CTypeSpecs::from_decl_specs(specs, _interner);
1002 let derived = CDerivedType::from_derived_decls_with_base_const(
1003 &declarator.derived, specs.qualifiers.is_const);
1004 TypeRepr::CType {
1005 specs: c_specs,
1006 derived,
1007 source: CTypeSource::Header,
1008 }
1009 }
1010
1011 pub fn from_type_name(
1016 type_name: &crate::ast::TypeName,
1017 interner: &crate::intern::StringInterner,
1018 ) -> Self {
1019 let c_specs = CTypeSpecs::from_decl_specs(&type_name.specs, interner);
1020 let derived = type_name.declarator
1021 .as_ref()
1022 .map(|d| CDerivedType::from_derived_decls_with_base_const(
1023 &d.derived, type_name.specs.qualifiers.is_const))
1024 .unwrap_or_default();
1025 TypeRepr::CType {
1026 specs: c_specs,
1027 derived,
1028 source: CTypeSource::Parser,
1029 }
1030 }
1031
1032 pub fn from_c_type_string(
1040 s: &str,
1041 interner: &crate::intern::StringInterner,
1042 files: &crate::source::FileRegistry,
1043 typedefs: &std::collections::HashSet<crate::intern::InternedStr>,
1044 ) -> Self {
1045 use crate::parser::parse_type_from_string;
1046
1047 match parse_type_from_string(s, interner, files, typedefs) {
1048 Ok(type_name) => Self::from_type_name(&type_name, interner),
1049 Err(_) => {
1050 let (specs, derived) = Self::parse_c_type_string(s, interner);
1052 TypeRepr::CType {
1053 specs,
1054 derived,
1055 source: CTypeSource::Apidoc { raw: s.to_string() },
1056 }
1057 }
1058 }
1059 }
1060
1061 fn parse_c_type_string(s: &str, interner: &crate::intern::StringInterner) -> (CTypeSpecs, Vec<CDerivedType>) {
1063 let s = s.trim();
1064
1065 let mut prefix_pointers: Vec<bool> = Vec::new(); let mut current = s;
1071 loop {
1072 if let Some(rest) = current.strip_prefix("*mut ") {
1073 prefix_pointers.push(false);
1074 current = rest.trim();
1075 } else if let Some(rest) = current.strip_prefix("*const ") {
1076 prefix_pointers.push(true);
1077 current = rest.trim();
1078 } else {
1079 break;
1080 }
1081 }
1082
1083 let mut ptr_count = 0;
1085 let mut is_const = false;
1086 let mut base = current;
1087
1088 while base.ends_with('*') {
1090 ptr_count += 1;
1091 base = base[..base.len() - 1].trim();
1092 }
1093
1094 if base.starts_with("const ") {
1096 is_const = true;
1097 base = base[6..].trim();
1098 }
1099 if base.ends_with(" const") {
1100 is_const = true;
1101 base = base[..base.len() - 6].trim();
1102 }
1103
1104 let specs = Self::parse_c_base_type(base, interner);
1106
1107 let mut derived: Vec<CDerivedType> = Vec::with_capacity(prefix_pointers.len() + ptr_count);
1112 for is_const_p in prefix_pointers.iter().rev() {
1113 derived.push(CDerivedType::Pointer {
1114 is_const: *is_const_p,
1115 is_volatile: false,
1116 is_restrict: false,
1117 });
1118 }
1119 for i in 0..ptr_count {
1120 derived.push(CDerivedType::Pointer {
1121 is_const: i + 1 == ptr_count && is_const,
1124 is_volatile: false,
1125 is_restrict: false,
1126 });
1127 }
1128
1129 (specs, derived)
1130 }
1131
1132 fn parse_c_base_type(s: &str, interner: &crate::intern::StringInterner) -> CTypeSpecs {
1134 match s {
1135 "void" => CTypeSpecs::Void,
1136 "char" => CTypeSpecs::Char { signed: None },
1137 "signed char" => CTypeSpecs::Char { signed: Some(true) },
1138 "unsigned char" => CTypeSpecs::Char { signed: Some(false) },
1139 "short" | "short int" | "signed short" | "signed short int" => {
1140 CTypeSpecs::Int { signed: true, size: IntSize::Short }
1141 }
1142 "unsigned short" | "unsigned short int" => {
1143 CTypeSpecs::Int { signed: false, size: IntSize::Short }
1144 }
1145 "int" | "signed" | "signed int" => {
1146 CTypeSpecs::Int { signed: true, size: IntSize::Int }
1147 }
1148 "unsigned" | "unsigned int" => {
1149 CTypeSpecs::Int { signed: false, size: IntSize::Int }
1150 }
1151 "long" | "long int" | "signed long" | "signed long int" => {
1152 CTypeSpecs::Int { signed: true, size: IntSize::Long }
1153 }
1154 "unsigned long" | "unsigned long int" => {
1155 CTypeSpecs::Int { signed: false, size: IntSize::Long }
1156 }
1157 "long long" | "long long int" | "signed long long" | "signed long long int" => {
1158 CTypeSpecs::Int { signed: true, size: IntSize::LongLong }
1159 }
1160 "unsigned long long" | "unsigned long long int" => {
1161 CTypeSpecs::Int { signed: false, size: IntSize::LongLong }
1162 }
1163 "float" => CTypeSpecs::Float,
1164 "double" => CTypeSpecs::Double { is_long: false },
1165 "long double" => CTypeSpecs::Double { is_long: true },
1166 "_Bool" | "bool" => CTypeSpecs::Bool,
1167 _ => {
1168 if let Some(rest) = s.strip_prefix("struct ") {
1170 if let Some(name) = interner.lookup(rest.trim()) {
1171 return CTypeSpecs::Struct { name: Some(name), is_union: false };
1172 }
1173 return CTypeSpecs::Struct { name: None, is_union: false };
1174 }
1175 if let Some(rest) = s.strip_prefix("union ") {
1176 if let Some(name) = interner.lookup(rest.trim()) {
1177 return CTypeSpecs::Struct { name: Some(name), is_union: true };
1178 }
1179 return CTypeSpecs::Struct { name: None, is_union: true };
1180 }
1181 if let Some(rest) = s.strip_prefix("enum ") {
1182 if let Some(name) = interner.lookup(rest.trim()) {
1183 return CTypeSpecs::Enum { name: Some(name) };
1184 }
1185 return CTypeSpecs::Enum { name: None };
1186 }
1187 if let Some(name) = interner.lookup(s) {
1189 CTypeSpecs::TypedefName(name)
1190 } else {
1191 CTypeSpecs::Void }
1195 }
1196 }
1197 }
1198
1199 pub fn to_display_string(&self, interner: &crate::intern::StringInterner) -> String {
1201 match self {
1202 TypeRepr::CType { specs, derived, .. } => {
1203 let base = specs.to_display_string(interner);
1204 let mut result = base;
1205 for d in derived {
1206 match d {
1207 CDerivedType::Pointer { is_const: true, .. } => result.push_str(" *const"),
1208 CDerivedType::Pointer { .. } => result.push_str(" *"),
1209 CDerivedType::Array { size: Some(n) } => {
1210 result.push_str(&format!("[{}]", n));
1211 }
1212 CDerivedType::Array { size: None } => result.push_str("[]"),
1213 CDerivedType::Function { .. } => result.push_str("()"),
1214 }
1215 }
1216 result
1217 }
1218 TypeRepr::RustType { repr, .. } => repr.to_display_string(),
1219 TypeRepr::Inferred(inferred) => inferred.to_display_string(interner),
1220 }
1221 }
1222
1223 pub fn to_rust_string(&self, interner: &crate::intern::StringInterner) -> String {
1225 match self {
1226 TypeRepr::CType { specs, derived, .. } => {
1227 let base = specs.to_rust_string(interner);
1228 let mut result = base;
1230 for d in derived.iter().rev() {
1231 if result == "()" && matches!(d, CDerivedType::Pointer { .. } | CDerivedType::Array { .. }) {
1233 result = "c_void".to_string();
1234 }
1235 result = match d {
1236 CDerivedType::Pointer { is_const: true, .. } => format!("*const {}", result),
1237 CDerivedType::Pointer { .. } => format!("*mut {}", result),
1238 CDerivedType::Array { size: Some(n) } => format!("[{}; {}]", result, n),
1239 CDerivedType::Array { size: None } => format!("*mut {}", result),
1240 CDerivedType::Function { .. } => format!("/* fn */"),
1241 };
1242 }
1243 result
1244 }
1245 TypeRepr::RustType { repr, .. } => repr.to_display_string(),
1246 TypeRepr::Inferred(inferred) => inferred.to_rust_string(interner),
1247 }
1248 }
1249}
1250
1251fn unified_to_c(
1258 ut: &crate::unified_type::UnifiedType,
1259 interner: &crate::intern::StringInterner,
1260) -> Option<(CTypeSpecs, Vec<CDerivedType>)> {
1261 use crate::unified_type::{UnifiedType as UT, IntSize as UIS};
1262
1263 match ut {
1264 UT::Void => Some((CTypeSpecs::Void, vec![])),
1265 UT::Bool => Some((CTypeSpecs::Bool, vec![])),
1266 UT::Char { signed } => Some((CTypeSpecs::Char { signed: *signed }, vec![])),
1267 UT::Int { signed, size } => {
1268 if matches!(size, UIS::Char) {
1271 return Some((CTypeSpecs::Char { signed: Some(*signed) }, vec![]));
1272 }
1273 let target = match size {
1274 UIS::Char => unreachable!(),
1275 UIS::Short => IntSize::Short,
1276 UIS::Int => IntSize::Int,
1277 UIS::Long => IntSize::Long,
1278 UIS::LongLong => IntSize::LongLong,
1279 UIS::Int128 => IntSize::Int128,
1280 };
1281 Some((CTypeSpecs::Int { signed: *signed, size: target }, vec![]))
1282 }
1283 UT::Float => Some((CTypeSpecs::Float, vec![])),
1284 UT::Double => Some((CTypeSpecs::Double { is_long: false }, vec![])),
1285 UT::LongDouble => Some((CTypeSpecs::Double { is_long: true }, vec![])),
1286 UT::Pointer { inner, is_const } => {
1287 let (specs, mut derived) = unified_to_c(inner, interner)?;
1288 derived.insert(
1290 0,
1291 CDerivedType::Pointer {
1292 is_const: *is_const,
1293 is_volatile: false,
1294 is_restrict: false,
1295 },
1296 );
1297 Some((specs, derived))
1298 }
1299 UT::Array { inner, size } => {
1300 let (specs, mut derived) = unified_to_c(inner, interner)?;
1301 derived.insert(0, CDerivedType::Array { size: *size });
1302 Some((specs, derived))
1303 }
1304 UT::Named(name) => {
1305 let specs = match interner.lookup(name) {
1306 Some(id) => CTypeSpecs::TypedefName(id),
1307 None => CTypeSpecs::UnknownTypedef(name.clone()),
1308 };
1309 Some((specs, vec![]))
1310 }
1311 UT::FnPtr { .. } | UT::Verbatim(_) | UT::Unknown => None,
1312 }
1313}
1314
1315impl TypeRepr {
1320 pub fn pointee_name(&self) -> Option<InternedStr> {
1325 match self {
1326 TypeRepr::CType { specs, derived, .. } => {
1327 if derived.iter().any(|d| matches!(d, CDerivedType::Pointer { .. })) {
1328 specs.type_name()
1329 } else {
1330 None
1331 }
1332 }
1333 TypeRepr::RustType { repr, .. } => repr.pointee_name(),
1334 TypeRepr::Inferred(inferred) => inferred.resolved_type()?.pointee_name(),
1335 }
1336 }
1337
1338 pub fn type_name(&self) -> Option<InternedStr> {
1343 match self {
1344 TypeRepr::CType { specs, .. } => specs.type_name(),
1345 TypeRepr::RustType { repr, .. } => repr.type_name(),
1346 TypeRepr::Inferred(inferred) => inferred.resolved_type()?.type_name(),
1347 }
1348 }
1349}
1350
1351impl CTypeSpecs {
1352 pub fn type_name(&self) -> Option<InternedStr> {
1354 match self {
1355 CTypeSpecs::Struct { name: Some(n), .. } => Some(*n),
1356 CTypeSpecs::TypedefName(n) => Some(*n),
1357 CTypeSpecs::Enum { name: Some(n) } => Some(*n),
1358 _ => None,
1359 }
1360 }
1361}
1362
1363impl InferredType {
1364 pub fn resolved_type(&self) -> Option<&TypeRepr> {
1369 match self {
1370 InferredType::Cast { target_type } => Some(target_type),
1371 InferredType::PtrMemberAccess { field_type: Some(ft), .. } => Some(ft),
1372 InferredType::MemberAccess { field_type: Some(ft), .. } => Some(ft),
1373 InferredType::ArraySubscript { element_type, .. } => Some(element_type),
1374 InferredType::AddressOf { inner_type } => Some(inner_type),
1375 InferredType::Dereference { pointer_type } => Some(pointer_type),
1376 InferredType::SymbolLookup { resolved_type, .. } => Some(resolved_type),
1377 InferredType::IncDec { inner_type } => Some(inner_type),
1378 InferredType::Assignment { lhs_type } => Some(lhs_type),
1379 InferredType::Comma { rhs_type } => Some(rhs_type),
1380 InferredType::Conditional { result_type, .. } => Some(result_type),
1381 InferredType::BinaryOp { result_type, .. } => Some(result_type),
1382 InferredType::UnaryArithmetic { inner_type } => Some(inner_type),
1383 InferredType::CompoundLiteral { type_name } => Some(type_name),
1384 InferredType::StmtExpr { last_expr_type } => last_expr_type.as_deref(),
1385 _ => None,
1386 }
1387 }
1388}
1389
1390impl RustTypeRepr {
1391 fn pointee_name(&self) -> Option<InternedStr> {
1396 None
1399 }
1400
1401 fn type_name(&self) -> Option<InternedStr> {
1403 None
1404 }
1405}
1406
1407impl CTypeSpecs {
1412 pub fn to_display_string(&self, interner: &crate::intern::StringInterner) -> String {
1414 match self {
1415 CTypeSpecs::Void => "void".to_string(),
1416 CTypeSpecs::Char { signed: None } => "char".to_string(),
1417 CTypeSpecs::Char { signed: Some(true) } => "signed char".to_string(),
1418 CTypeSpecs::Char { signed: Some(false) } => "unsigned char".to_string(),
1419 CTypeSpecs::Int { signed: true, size: IntSize::Short } => "short".to_string(),
1420 CTypeSpecs::Int { signed: false, size: IntSize::Short } => "unsigned short".to_string(),
1421 CTypeSpecs::Int { signed: true, size: IntSize::Int } => "int".to_string(),
1422 CTypeSpecs::Int { signed: false, size: IntSize::Int } => "unsigned int".to_string(),
1423 CTypeSpecs::Int { signed: true, size: IntSize::Long } => "long".to_string(),
1424 CTypeSpecs::Int { signed: false, size: IntSize::Long } => "unsigned long".to_string(),
1425 CTypeSpecs::Int { signed: true, size: IntSize::LongLong } => "long long".to_string(),
1426 CTypeSpecs::Int { signed: false, size: IntSize::LongLong } => "unsigned long long".to_string(),
1427 CTypeSpecs::Int { signed: true, size: IntSize::Int128 } => "__int128".to_string(),
1428 CTypeSpecs::Int { signed: false, size: IntSize::Int128 } => "unsigned __int128".to_string(),
1429 CTypeSpecs::Float => "float".to_string(),
1430 CTypeSpecs::Double { is_long: false } => "double".to_string(),
1431 CTypeSpecs::Double { is_long: true } => "long double".to_string(),
1432 CTypeSpecs::Bool => "_Bool".to_string(),
1433 CTypeSpecs::Struct { name: Some(n), is_union: false } => {
1434 format!("struct {}", interner.get(*n))
1435 }
1436 CTypeSpecs::Struct { name: None, is_union: false } => "struct".to_string(),
1437 CTypeSpecs::Struct { name: Some(n), is_union: true } => {
1438 format!("union {}", interner.get(*n))
1439 }
1440 CTypeSpecs::Struct { name: None, is_union: true } => "union".to_string(),
1441 CTypeSpecs::Enum { name: Some(n) } => format!("enum {}", interner.get(*n)),
1442 CTypeSpecs::Enum { name: None } => "enum".to_string(),
1443 CTypeSpecs::TypedefName(n) => interner.get(*n).to_string(),
1444 CTypeSpecs::UnknownTypedef(s) => s.clone(),
1445 }
1446 }
1447
1448 pub fn to_rust_string(&self, interner: &crate::intern::StringInterner) -> String {
1450 match self {
1451 CTypeSpecs::Void => "()".to_string(),
1452 CTypeSpecs::Char { signed: None } => "c_char".to_string(),
1453 CTypeSpecs::Char { signed: Some(true) } => "c_schar".to_string(),
1454 CTypeSpecs::Char { signed: Some(false) } => "c_uchar".to_string(),
1455 CTypeSpecs::Int { signed: true, size: IntSize::Short } => "c_short".to_string(),
1456 CTypeSpecs::Int { signed: false, size: IntSize::Short } => "c_ushort".to_string(),
1457 CTypeSpecs::Int { signed: true, size: IntSize::Int } => "c_int".to_string(),
1458 CTypeSpecs::Int { signed: false, size: IntSize::Int } => "c_uint".to_string(),
1459 CTypeSpecs::Int { signed: true, size: IntSize::Long } => "c_long".to_string(),
1460 CTypeSpecs::Int { signed: false, size: IntSize::Long } => "c_ulong".to_string(),
1461 CTypeSpecs::Int { signed: true, size: IntSize::LongLong } => "c_longlong".to_string(),
1462 CTypeSpecs::Int { signed: false, size: IntSize::LongLong } => "c_ulonglong".to_string(),
1463 CTypeSpecs::Int { signed: true, size: IntSize::Int128 } => "i128".to_string(),
1464 CTypeSpecs::Int { signed: false, size: IntSize::Int128 } => "u128".to_string(),
1465 CTypeSpecs::Float => "c_float".to_string(),
1466 CTypeSpecs::Double { is_long: false } => "c_double".to_string(),
1467 CTypeSpecs::Double { is_long: true } => "c_double".to_string(), CTypeSpecs::Bool => "bool".to_string(),
1469 CTypeSpecs::Struct { name: Some(n), .. } => interner.get(*n).to_string(),
1470 CTypeSpecs::Struct { name: None, .. } => "/* anonymous struct */".to_string(),
1471 CTypeSpecs::Enum { name: Some(n) } => interner.get(*n).to_string(),
1472 CTypeSpecs::Enum { name: None } => "/* anonymous enum */".to_string(),
1473 CTypeSpecs::TypedefName(n) => interner.get(*n).to_string(),
1474 CTypeSpecs::UnknownTypedef(s) => s.clone(),
1475 }
1476 }
1477}
1478
1479impl RustTypeRepr {
1480 pub fn make_outer_pointer_const(&mut self) {
1482 if let RustTypeRepr::Pointer { is_const, .. } = self {
1483 *is_const = true;
1484 }
1485 }
1486
1487 pub fn has_outer_pointer(&self) -> bool {
1489 matches!(self, RustTypeRepr::Pointer { .. })
1490 }
1491}
1492
1493impl RustTypeRepr {
1494 pub fn to_display_string(&self) -> String {
1496 match self {
1497 RustTypeRepr::CPrimitive(kind) => kind.to_string(),
1498 RustTypeRepr::RustPrimitive(kind) => kind.to_string(),
1499 RustTypeRepr::Pointer { inner, is_const: true } => {
1500 format!("*const {}", inner.to_display_string())
1501 }
1502 RustTypeRepr::Pointer { inner, is_const: false } => {
1503 format!("*mut {}", inner.to_display_string())
1504 }
1505 RustTypeRepr::Reference { inner, is_mut: true } => {
1506 format!("&mut {}", inner.to_display_string())
1507 }
1508 RustTypeRepr::Reference { inner, is_mut: false } => {
1509 format!("&{}", inner.to_display_string())
1510 }
1511 RustTypeRepr::Named(name) => name.clone(),
1512 RustTypeRepr::Option(inner) => format!("Option<{}>", inner.to_display_string()),
1513 RustTypeRepr::FnPointer { params, ret } => {
1514 let params_str: Vec<_> = params.iter().map(|p| p.to_display_string()).collect();
1515 let ret_str = ret
1516 .as_ref()
1517 .map(|r| format!(" -> {}", r.to_display_string()))
1518 .unwrap_or_default();
1519 format!("fn({}){}", params_str.join(", "), ret_str)
1520 }
1521 RustTypeRepr::Unit => "()".to_string(),
1522 RustTypeRepr::Unknown(s) => s.clone(),
1523 }
1524 }
1525}
1526
1527impl fmt::Display for CPrimitiveKind {
1528 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1529 let s = match self {
1530 CPrimitiveKind::CChar => "c_char",
1531 CPrimitiveKind::CSchar => "c_schar",
1532 CPrimitiveKind::CUchar => "c_uchar",
1533 CPrimitiveKind::CShort => "c_short",
1534 CPrimitiveKind::CUshort => "c_ushort",
1535 CPrimitiveKind::CInt => "c_int",
1536 CPrimitiveKind::CUint => "c_uint",
1537 CPrimitiveKind::CLong => "c_long",
1538 CPrimitiveKind::CUlong => "c_ulong",
1539 CPrimitiveKind::CLongLong => "c_longlong",
1540 CPrimitiveKind::CUlongLong => "c_ulonglong",
1541 CPrimitiveKind::CFloat => "c_float",
1542 CPrimitiveKind::CDouble => "c_double",
1543 };
1544 write!(f, "{}", s)
1545 }
1546}
1547
1548impl fmt::Display for RustPrimitiveKind {
1549 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1550 let s = match self {
1551 RustPrimitiveKind::I8 => "i8",
1552 RustPrimitiveKind::I16 => "i16",
1553 RustPrimitiveKind::I32 => "i32",
1554 RustPrimitiveKind::I64 => "i64",
1555 RustPrimitiveKind::I128 => "i128",
1556 RustPrimitiveKind::Isize => "isize",
1557 RustPrimitiveKind::U8 => "u8",
1558 RustPrimitiveKind::U16 => "u16",
1559 RustPrimitiveKind::U32 => "u32",
1560 RustPrimitiveKind::U64 => "u64",
1561 RustPrimitiveKind::U128 => "u128",
1562 RustPrimitiveKind::Usize => "usize",
1563 RustPrimitiveKind::F32 => "f32",
1564 RustPrimitiveKind::F64 => "f64",
1565 RustPrimitiveKind::Bool => "bool",
1566 };
1567 write!(f, "{}", s)
1568 }
1569}
1570
1571impl InferredType {
1572 pub fn to_display_string(&self, interner: &crate::intern::StringInterner) -> String {
1574 match self {
1575 InferredType::IntLiteral => "int".to_string(),
1576 InferredType::UIntLiteral => "unsigned int".to_string(),
1577 InferredType::FloatLiteral => "double".to_string(),
1578 InferredType::CharLiteral => "int".to_string(),
1579 InferredType::StringLiteral => "char *".to_string(),
1580 InferredType::SymbolLookup { resolved_type, .. } => {
1581 resolved_type.to_display_string(interner)
1582 }
1583 InferredType::ThxDefault => "*mut PerlInterpreter".to_string(),
1584 InferredType::BinaryOp { result_type, .. } => result_type.to_display_string(interner),
1585 InferredType::UnaryArithmetic { inner_type } => inner_type.to_display_string(interner),
1586 InferredType::LogicalNot => "int".to_string(),
1587 InferredType::AddressOf { inner_type } => {
1588 format!("{} *", inner_type.to_display_string(interner))
1589 }
1590 InferredType::Dereference { pointer_type } => {
1591 let s = pointer_type.to_display_string(interner);
1592 s.trim_end_matches(" *").to_string()
1593 }
1594 InferredType::IncDec { inner_type } => inner_type.to_display_string(interner),
1595 InferredType::MemberAccess { field_type: Some(ft), .. } => {
1596 ft.to_display_string(interner)
1597 }
1598 InferredType::MemberAccess { base_type, member, .. } => {
1599 format!("{}.{}", base_type, interner.get(*member))
1600 }
1601 InferredType::PtrMemberAccess { field_type: Some(ft), .. } => {
1602 ft.to_display_string(interner)
1603 }
1604 InferredType::PtrMemberAccess { base_type, member, .. } => {
1605 format!("{}->{}", base_type, interner.get(*member))
1606 }
1607 InferredType::ArraySubscript { element_type, .. } => {
1608 element_type.to_display_string(interner)
1609 }
1610 InferredType::Conditional { result_type, .. } => {
1611 result_type.to_display_string(interner)
1612 }
1613 InferredType::Comma { rhs_type } => rhs_type.to_display_string(interner),
1614 InferredType::Assignment { lhs_type } => lhs_type.to_display_string(interner),
1615 InferredType::Cast { target_type } => target_type.to_display_string(interner),
1616 InferredType::Sizeof | InferredType::Alignof => "unsigned long".to_string(),
1617 InferredType::CompoundLiteral { type_name } => type_name.to_display_string(interner),
1618 InferredType::StmtExpr { last_expr_type: Some(t) } => t.to_display_string(interner),
1619 InferredType::StmtExpr { last_expr_type: None } => "void".to_string(),
1620 InferredType::Assert => "void".to_string(),
1621 InferredType::FunctionReturn { func_name } => {
1622 format!("{}()", interner.get(*func_name))
1623 }
1624 }
1625 }
1626
1627 pub fn to_rust_string(&self, interner: &crate::intern::StringInterner) -> String {
1629 match self {
1630 InferredType::IntLiteral => "c_int".to_string(),
1631 InferredType::UIntLiteral => "c_uint".to_string(),
1632 InferredType::FloatLiteral => "c_double".to_string(),
1633 InferredType::CharLiteral => "c_int".to_string(),
1634 InferredType::StringLiteral => "*const c_char".to_string(),
1635 InferredType::SymbolLookup { resolved_type, .. } => {
1636 resolved_type.to_rust_string(interner)
1637 }
1638 InferredType::ThxDefault => "*mut PerlInterpreter".to_string(),
1639 InferredType::BinaryOp { result_type, .. } => result_type.to_rust_string(interner),
1640 InferredType::UnaryArithmetic { inner_type } => inner_type.to_rust_string(interner),
1641 InferredType::LogicalNot => "c_int".to_string(),
1642 InferredType::AddressOf { inner_type } => {
1643 format!("*mut {}", inner_type.to_rust_string(interner))
1644 }
1645 InferredType::Dereference { pointer_type } => {
1646 let s = pointer_type.to_rust_string(interner);
1647 s.strip_prefix("*mut ").or_else(|| s.strip_prefix("*const "))
1649 .unwrap_or(&s).to_string()
1650 }
1651 InferredType::IncDec { inner_type } => inner_type.to_rust_string(interner),
1652 InferredType::MemberAccess { field_type: Some(ft), .. } => {
1653 ft.to_rust_string(interner)
1654 }
1655 InferredType::MemberAccess { base_type, member, .. } => {
1656 format!("/* {}.{} */", base_type, interner.get(*member))
1657 }
1658 InferredType::PtrMemberAccess { field_type: Some(ft), .. } => {
1659 ft.to_rust_string(interner)
1660 }
1661 InferredType::PtrMemberAccess { base_type, member, .. } => {
1662 format!("/* {}->{} */", base_type, interner.get(*member))
1663 }
1664 InferredType::ArraySubscript { element_type, .. } => {
1665 element_type.to_rust_string(interner)
1666 }
1667 InferredType::Conditional { result_type, .. } => {
1668 result_type.to_rust_string(interner)
1669 }
1670 InferredType::Comma { rhs_type } => rhs_type.to_rust_string(interner),
1671 InferredType::Assignment { lhs_type } => lhs_type.to_rust_string(interner),
1672 InferredType::Cast { target_type } => target_type.to_rust_string(interner),
1673 InferredType::Sizeof | InferredType::Alignof => "c_ulong".to_string(),
1674 InferredType::CompoundLiteral { type_name } => type_name.to_rust_string(interner),
1675 InferredType::StmtExpr { last_expr_type: Some(t) } => t.to_rust_string(interner),
1676 InferredType::StmtExpr { last_expr_type: None } => "()".to_string(),
1677 InferredType::Assert => "()".to_string(),
1678 InferredType::FunctionReturn { func_name } => {
1679 format!("/* {}() ret */", interner.get(*func_name))
1680 }
1681 }
1682 }
1683}
1684
1685#[cfg(test)]
1690mod tests {
1691 use super::*;
1692
1693 fn ptr(is_const: bool) -> CDerivedType {
1694 CDerivedType::Pointer { is_const, is_volatile: false, is_restrict: false }
1695 }
1696
1697 fn const_flags(derived: &[CDerivedType]) -> Vec<bool> {
1698 derived.iter().map(|d| match d {
1699 CDerivedType::Pointer { is_const, .. } => *is_const,
1700 _ => unreachable!(),
1701 }).collect()
1702 }
1703
1704 #[test]
1706 fn test_shift_pointee_const() {
1707 let mut d = vec![ptr(false)];
1709 shift_pointee_const(&mut d, true);
1710 assert_eq!(const_flags(&d), vec![true]);
1711
1712 let mut d = vec![ptr(false)];
1714 shift_pointee_const(&mut d, false);
1715 assert_eq!(const_flags(&d), vec![false]);
1716
1717 let mut d = vec![ptr(true)];
1719 shift_pointee_const(&mut d, false);
1720 assert_eq!(const_flags(&d), vec![false]);
1721
1722 let mut d = vec![ptr(false), ptr(false)];
1724 shift_pointee_const(&mut d, true);
1725 assert_eq!(const_flags(&d), vec![false, true]);
1726
1727 let mut d = vec![ptr(false), ptr(true)];
1730 shift_pointee_const(&mut d, false);
1731 assert_eq!(const_flags(&d), vec![true, false]);
1732 }
1733
1734 #[test]
1735 fn test_rust_type_repr_from_string() {
1736 assert!(matches!(
1737 RustTypeRepr::from_type_string("c_int"),
1738 RustTypeRepr::CPrimitive(CPrimitiveKind::CInt)
1739 ));
1740
1741 assert!(matches!(
1742 RustTypeRepr::from_type_string("i32"),
1743 RustTypeRepr::RustPrimitive(RustPrimitiveKind::I32)
1744 ));
1745
1746 assert!(matches!(
1747 RustTypeRepr::from_type_string("()"),
1748 RustTypeRepr::Unit
1749 ));
1750
1751 if let RustTypeRepr::Pointer { inner, is_const: false } =
1752 RustTypeRepr::from_type_string("*mut SV")
1753 {
1754 assert!(matches!(*inner, RustTypeRepr::Named(ref n) if n == "SV"));
1755 } else {
1756 panic!("Expected *mut SV");
1757 }
1758
1759 if let RustTypeRepr::Pointer { inner, is_const: true } =
1760 RustTypeRepr::from_type_string("*const c_char")
1761 {
1762 assert!(matches!(*inner, RustTypeRepr::CPrimitive(CPrimitiveKind::CChar)));
1763 } else {
1764 panic!("Expected *const c_char");
1765 }
1766 }
1767
1768 #[test]
1769 fn test_rust_type_repr_from_string_with_spaces() {
1770 if let RustTypeRepr::Pointer { inner, is_const: false } =
1772 RustTypeRepr::from_type_string("* mut SV")
1773 {
1774 assert!(matches!(*inner, RustTypeRepr::Named(ref n) if n == "SV"));
1775 } else {
1776 panic!("Expected * mut SV");
1777 }
1778 }
1779
1780 #[test]
1781 fn test_c_primitive_display() {
1782 assert_eq!(CPrimitiveKind::CInt.to_string(), "c_int");
1783 assert_eq!(CPrimitiveKind::CUlong.to_string(), "c_ulong");
1784 }
1785
1786 #[test]
1787 fn test_rust_primitive_display() {
1788 assert_eq!(RustPrimitiveKind::I32.to_string(), "i32");
1789 assert_eq!(RustPrimitiveKind::Usize.to_string(), "usize");
1790 }
1791
1792 fn make_void_ptr() -> TypeRepr {
1795 TypeRepr::CType {
1796 specs: CTypeSpecs::Void,
1797 derived: vec![CDerivedType::Pointer {
1798 is_const: false,
1799 is_volatile: false,
1800 is_restrict: false,
1801 }],
1802 source: CTypeSource::Apidoc { raw: "void *".to_string() },
1803 }
1804 }
1805
1806 fn make_concrete_ptr() -> TypeRepr {
1807 TypeRepr::CType {
1808 specs: CTypeSpecs::Char { signed: None },
1809 derived: vec![CDerivedType::Pointer {
1810 is_const: false,
1811 is_volatile: false,
1812 is_restrict: false,
1813 }],
1814 source: CTypeSource::Apidoc { raw: "char *".to_string() },
1815 }
1816 }
1817
1818 #[test]
1819 fn test_void_pointer_structural() {
1820 let vp = make_void_ptr();
1821 assert!(vp.is_pointer_type());
1822 assert!(vp.is_void_pointer());
1823 assert!(!vp.is_concrete_pointer());
1824 }
1825
1826 #[test]
1827 fn test_concrete_pointer_structural() {
1828 let cp = make_concrete_ptr();
1829 assert!(cp.is_pointer_type());
1830 assert!(!cp.is_void_pointer());
1831 assert!(cp.is_concrete_pointer());
1832 }
1833
1834 #[test]
1835 fn test_inferred_member_access_pointer_recursive() {
1836 let inner = make_concrete_ptr();
1840 let inferred = TypeRepr::Inferred(InferredType::MemberAccess {
1841 base_type: "xpvcv".to_string(),
1842 member: crate::intern::StringInterner::new().intern("foo"),
1843 field_type: Some(Box::new(inner)),
1844 });
1845 assert!(inferred.is_pointer_type());
1846 assert!(!inferred.is_void_pointer());
1847 assert!(inferred.is_concrete_pointer());
1848 assert!(!inferred.has_outer_pointer());
1850 }
1851}