1use std::collections::{HashMap, HashSet};
6
7use crate::apidoc::ApidocDict;
8use crate::ast::*;
9use crate::fields_dict::FieldsDict;
10use crate::inline_fn::InlineFnDict;
11use crate::intern::{InternedStr, StringInterner};
12use crate::parser::parse_type_from_string;
13use crate::rust_decl::RustDeclDict;
14use crate::source::{FileRegistry, SourceLocation};
15use crate::type_env::{TypeEnv, TypeConstraint as TypeEnvConstraint};
16use crate::type_repr::{
17 CTypeSource, CTypeSpecs, CDerivedType, InferredType,
18 RustTypeRepr, RustTypeSource, TypeRepr,
19};
20use crate::unified_type::{IntSize, UnifiedType};
21
22fn leftmost_param_ident(
30 expr: &Expr,
31 params: &HashSet<InternedStr>,
32) -> Option<(InternedStr, ExprId)> {
33 let mut cur = expr;
34 loop {
35 match &cur.kind {
36 ExprKind::Member { expr: base, .. }
37 | ExprKind::PtrMember { expr: base, .. }
38 | ExprKind::Deref(base)
39 | ExprKind::Cast { expr: base, .. } => cur = base,
40 ExprKind::StmtExpr(compound) => {
41 if let Some(inner) = mutable_ptr_inner_expr(compound) {
42 cur = inner;
43 } else {
44 return None;
45 }
46 }
47 ExprKind::Ident(name) if params.contains(name) => {
48 return Some((*name, cur.id));
49 }
50 _ => return None,
51 }
52 }
53}
54
55fn mutable_ptr_inner_expr(compound: &CompoundStmt) -> Option<&Expr> {
59 if compound.items.len() != 2 {
60 return None;
61 }
62 let decl = match &compound.items[0] {
63 BlockItem::Decl(d) => d,
64 _ => return None,
65 };
66 if decl.declarators.len() != 1 {
67 return None;
68 }
69 let init_decl = &decl.declarators[0];
70 let declared_name = init_decl.declarator.name?;
71 let init_expr = match init_decl.init.as_ref()? {
72 Initializer::Expr(e) => e.as_ref(),
73 _ => return None,
74 };
75 let last_expr = match &compound.items[1] {
76 BlockItem::Stmt(Stmt::Expr(Some(e), _)) => e,
77 _ => return None,
78 };
79 if let ExprKind::Ident(name) = &last_expr.kind {
80 if *name == declared_name {
81 return Some(init_expr);
82 }
83 }
84 None
85}
86
87fn is_anonymous_struct_or_union_field(t: &TypeRepr) -> bool {
96 if let TypeRepr::CType { specs, derived, .. } = t {
97 if !derived.is_empty() {
98 return false;
99 }
100 return matches!(
101 specs,
102 crate::type_repr::CTypeSpecs::Struct { name: None, .. }
103 );
104 }
105 false
106}
107
108fn extract_struct_name_str(t: &TypeRepr, interner: &crate::intern::StringInterner) -> Option<String> {
115 use crate::type_repr::{CTypeSpecs, RustTypeRepr};
116 match t {
117 TypeRepr::CType { specs, derived, .. } if derived.is_empty() => match specs {
118 CTypeSpecs::TypedefName(n) => Some(interner.get(*n).to_string()),
119 CTypeSpecs::Struct { name: Some(n), .. } => Some(interner.get(*n).to_string()),
120 _ => None,
121 },
122 TypeRepr::RustType { repr, .. } => match repr {
123 RustTypeRepr::Named(s) => Some(s.clone()),
124 _ => None,
125 },
126 TypeRepr::Inferred(inferred) => {
127 inferred.resolved_type().and_then(|t| extract_struct_name_str(t, interner))
128 }
129 _ => None,
130 }
131}
132
133fn wrap_with_outer_pointer(ty: TypeRepr, is_const: bool) -> TypeRepr {
138 use crate::type_repr::CDerivedType;
139 match ty {
140 TypeRepr::CType { specs, mut derived, source } => {
141 derived.push(CDerivedType::Pointer { is_const, is_volatile: false, is_restrict: false });
142 TypeRepr::CType { specs, derived, source }
143 }
144 other => other,
145 }
146}
147
148#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
150pub struct TypeVar(usize);
151
152#[derive(Debug, Clone)]
154pub enum TypeConstraint {
155 FunctionArg {
157 var: TypeVar,
158 func_name: InternedStr,
159 arg_index: usize,
160 },
161 HasField {
163 var: TypeVar,
164 field: InternedStr,
165 },
166}
167
168#[derive(Debug, Clone, PartialEq)]
170pub enum Type {
171 Void,
173 Char,
175 SignedChar,
177 UnsignedChar,
179 Short,
181 UnsignedShort,
183 Int,
185 UnsignedInt,
187 Long,
189 UnsignedLong,
191 LongLong,
193 UnsignedLongLong,
195 Float,
197 Double,
199 LongDouble,
201 Bool,
203 Int128,
205 UnsignedInt128,
207 Pointer(Box<Type>, TypeQualifiers),
209 Array(Box<Type>, Option<usize>),
211 Function {
213 return_type: Box<Type>,
214 params: Vec<Type>,
215 variadic: bool,
216 },
217 Struct {
219 name: Option<InternedStr>,
220 members: Option<Vec<(InternedStr, Type)>>,
222 },
223 Union {
225 name: Option<InternedStr>,
226 members: Option<Vec<(InternedStr, Type)>>,
227 },
228 Enum {
230 name: Option<InternedStr>,
231 },
232 TypedefName(InternedStr),
234 Unknown,
236}
237
238impl Type {
239 pub fn display(&self, interner: &StringInterner) -> String {
241 match self {
242 Type::Void => "void".to_string(),
243 Type::Char => "char".to_string(),
244 Type::SignedChar => "signed char".to_string(),
245 Type::UnsignedChar => "unsigned char".to_string(),
246 Type::Short => "short".to_string(),
247 Type::UnsignedShort => "unsigned short".to_string(),
248 Type::Int => "int".to_string(),
249 Type::UnsignedInt => "unsigned int".to_string(),
250 Type::Long => "long".to_string(),
251 Type::UnsignedLong => "unsigned long".to_string(),
252 Type::LongLong => "long long".to_string(),
253 Type::UnsignedLongLong => "unsigned long long".to_string(),
254 Type::Float => "float".to_string(),
255 Type::Double => "double".to_string(),
256 Type::LongDouble => "long double".to_string(),
257 Type::Bool => "_Bool".to_string(),
258 Type::Int128 => "__int128".to_string(),
259 Type::UnsignedInt128 => "unsigned __int128".to_string(),
260 Type::Pointer(inner, quals) => {
261 let mut s = inner.display(interner);
262 s.push('*');
263 if quals.is_const {
264 s.push_str(" const");
265 }
266 if quals.is_volatile {
267 s.push_str(" volatile");
268 }
269 if quals.is_restrict {
270 s.push_str(" restrict");
271 }
272 s
273 }
274 Type::Array(inner, size) => {
275 let inner_s = inner.display(interner);
276 match size {
277 Some(n) => format!("{}[{}]", inner_s, n),
278 None => format!("{}[]", inner_s),
279 }
280 }
281 Type::Function { return_type, params, variadic } => {
282 let params_s: Vec<_> = params.iter()
283 .map(|p| p.display(interner))
284 .collect();
285 let mut s = format!("(function {} ({}))", return_type.display(interner), params_s.join(", "));
286 if *variadic {
287 s = s.replace("))", ", ...))");
288 }
289 s
290 }
291 Type::Struct { name, .. } => {
292 match name {
293 Some(n) => format!("struct {}", interner.get(*n)),
294 None => "struct <anonymous>".to_string(),
295 }
296 }
297 Type::Union { name, .. } => {
298 match name {
299 Some(n) => format!("union {}", interner.get(*n)),
300 None => "union <anonymous>".to_string(),
301 }
302 }
303 Type::Enum { name } => {
304 match name {
305 Some(n) => format!("enum {}", interner.get(*n)),
306 None => "enum <anonymous>".to_string(),
307 }
308 }
309 Type::TypedefName(name) => interner.get(*name).to_string(),
310 Type::Unknown => "<unknown>".to_string(),
311 }
312 }
313
314 pub fn is_integer(&self) -> bool {
316 matches!(
317 self,
318 Type::Char
319 | Type::SignedChar
320 | Type::UnsignedChar
321 | Type::Short
322 | Type::UnsignedShort
323 | Type::Int
324 | Type::UnsignedInt
325 | Type::Long
326 | Type::UnsignedLong
327 | Type::LongLong
328 | Type::UnsignedLongLong
329 | Type::Bool
330 | Type::Int128
331 | Type::UnsignedInt128
332 | Type::Enum { .. }
333 )
334 }
335
336 pub fn is_floating(&self) -> bool {
338 matches!(self, Type::Float | Type::Double | Type::LongDouble)
339 }
340
341 pub fn is_arithmetic(&self) -> bool {
343 self.is_integer() || self.is_floating()
344 }
345
346 pub fn is_pointer(&self) -> bool {
348 matches!(self, Type::Pointer(_, _))
349 }
350
351 pub fn to_unified(&self, interner: &StringInterner) -> UnifiedType {
353 match self {
354 Type::Void => UnifiedType::Void,
355 Type::Bool => UnifiedType::Bool,
356
357 Type::Char => UnifiedType::Char { signed: None },
358 Type::SignedChar => UnifiedType::Char { signed: Some(true) },
359 Type::UnsignedChar => UnifiedType::Char { signed: Some(false) },
360
361 Type::Short => UnifiedType::Int { signed: true, size: IntSize::Short },
362 Type::UnsignedShort => UnifiedType::Int { signed: false, size: IntSize::Short },
363 Type::Int => UnifiedType::Int { signed: true, size: IntSize::Int },
364 Type::UnsignedInt => UnifiedType::Int { signed: false, size: IntSize::Int },
365 Type::Long => UnifiedType::Int { signed: true, size: IntSize::Long },
366 Type::UnsignedLong => UnifiedType::Int { signed: false, size: IntSize::Long },
367 Type::LongLong => UnifiedType::Int { signed: true, size: IntSize::LongLong },
368 Type::UnsignedLongLong => UnifiedType::Int { signed: false, size: IntSize::LongLong },
369 Type::Int128 => UnifiedType::Int { signed: true, size: IntSize::Int128 },
370 Type::UnsignedInt128 => UnifiedType::Int { signed: false, size: IntSize::Int128 },
371
372 Type::Float => UnifiedType::Float,
373 Type::Double => UnifiedType::Double,
374 Type::LongDouble => UnifiedType::LongDouble,
375
376 Type::Pointer(inner, quals) => UnifiedType::Pointer {
377 inner: Box::new(inner.to_unified(interner)),
378 is_const: quals.is_const,
379 },
380
381 Type::Array(inner, size) => UnifiedType::Array {
382 inner: Box::new(inner.to_unified(interner)),
383 size: *size,
384 },
385
386 Type::Struct { name: Some(n), .. } => {
387 UnifiedType::Named(interner.get(*n).to_string())
388 }
389 Type::Struct { name: None, .. } => UnifiedType::Unknown,
390
391 Type::Union { name: Some(n), .. } => {
392 UnifiedType::Named(interner.get(*n).to_string())
393 }
394 Type::Union { name: None, .. } => UnifiedType::Unknown,
395
396 Type::Enum { name: Some(n) } => {
397 UnifiedType::Named(interner.get(*n).to_string())
398 }
399 Type::Enum { name: None } => UnifiedType::Int { signed: true, size: IntSize::Int },
400
401 Type::TypedefName(name) => {
402 UnifiedType::Named(interner.get(*name).to_string())
403 }
404
405 Type::Function { .. } => UnifiedType::Unknown, Type::Unknown => UnifiedType::Unknown,
408 }
409 }
410}
411
412#[derive(Debug, Clone)]
414pub struct Symbol {
415 pub name: InternedStr,
417 pub ty: Type,
419 pub loc: SourceLocation,
421 pub kind: SymbolKind,
423}
424
425#[derive(Debug, Clone, PartialEq)]
427pub enum SymbolKind {
428 Variable,
430 Function,
432 Typedef,
434 EnumConstant(i64),
436}
437
438#[derive(Debug)]
440pub struct Scope {
441 symbols: HashMap<InternedStr, Symbol>,
443 parent: Option<ScopeId>,
445}
446
447impl Scope {
448 fn new(parent: Option<ScopeId>) -> Self {
449 Self {
450 symbols: HashMap::new(),
451 parent,
452 }
453 }
454}
455
456#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
458pub struct ScopeId(usize);
459
460pub struct SemanticAnalyzer<'a> {
462 interner: &'a StringInterner,
464 scopes: Vec<Scope>,
466 current_scope: ScopeId,
468 struct_defs: HashMap<InternedStr, Vec<(InternedStr, Type)>>,
470 union_defs: HashMap<InternedStr, Vec<(InternedStr, Type)>>,
472 typedef_defs: HashMap<InternedStr, Type>,
474 apidoc: Option<&'a ApidocDict>,
476 fields_dict: Option<&'a FieldsDict>,
478 rust_decl_dict: Option<&'a RustDeclDict>,
480 inline_fn_dict: Option<&'a InlineFnDict>,
482 type_vars: HashMap<InternedStr, TypeVar>,
484 next_type_var: usize,
486 constraints: Vec<TypeConstraint>,
488 constraint_mode: bool,
490 macro_params: HashSet<InternedStr>,
492 macro_return_types: Option<&'a HashMap<String, String>>,
494 macro_param_types: Option<&'a HashMap<String, Vec<(String, String)>>>,
497 files: Option<&'a FileRegistry>,
499 parser_typedefs: Option<&'a HashSet<InternedStr>>,
501}
502
503impl<'a> SemanticAnalyzer<'a> {
504 pub fn new(
506 interner: &'a StringInterner,
507 apidoc: Option<&'a ApidocDict>,
508 fields_dict: Option<&'a FieldsDict>,
509 ) -> Self {
510 Self::with_rust_decl_dict(interner, apidoc, fields_dict, None, None)
511 }
512
513 pub fn with_rust_decl_dict(
515 interner: &'a StringInterner,
516 apidoc: Option<&'a ApidocDict>,
517 fields_dict: Option<&'a FieldsDict>,
518 rust_decl_dict: Option<&'a RustDeclDict>,
519 inline_fn_dict: Option<&'a InlineFnDict>,
520 ) -> Self {
521 let global_scope = Scope::new(None);
522 Self {
523 interner,
524 scopes: vec![global_scope],
525 current_scope: ScopeId(0),
526 struct_defs: HashMap::new(),
527 union_defs: HashMap::new(),
528 typedef_defs: HashMap::new(),
529 apidoc,
530 fields_dict,
531 rust_decl_dict,
532 inline_fn_dict,
533 type_vars: HashMap::new(),
534 next_type_var: 0,
535 constraints: Vec::new(),
536 constraint_mode: false,
537 macro_params: HashSet::new(),
538 macro_return_types: None,
539 macro_param_types: None,
540 files: None,
541 parser_typedefs: None,
542 }
543 }
544
545 pub fn set_macro_return_types(&mut self, cache: &'a HashMap<String, String>) {
547 self.macro_return_types = Some(cache);
548 }
549
550 pub fn set_macro_param_types(&mut self, cache: &'a HashMap<String, Vec<(String, String)>>) {
552 self.macro_param_types = Some(cache);
553 }
554
555 pub fn get_macro_return_type(&self, macro_name: &str) -> Option<&str> {
557 self.macro_return_types
558 .and_then(|cache| cache.get(macro_name))
559 .map(|s| s.as_str())
560 }
561
562 pub fn get_macro_param_types(&self, macro_name: &str) -> Option<&Vec<(String, String)>> {
564 self.macro_param_types
565 .and_then(|cache| cache.get(macro_name))
566 }
567
568 pub fn push_scope(&mut self) {
570 let new_scope = Scope::new(Some(self.current_scope));
571 let new_id = ScopeId(self.scopes.len());
572 self.scopes.push(new_scope);
573 self.current_scope = new_id;
574 }
575
576 pub fn pop_scope(&mut self) {
578 if let Some(parent) = self.scopes[self.current_scope.0].parent {
579 self.current_scope = parent;
580 }
581 }
582
583 pub fn define_symbol(&mut self, symbol: Symbol) {
585 let scope = &mut self.scopes[self.current_scope.0];
586 scope.symbols.insert(symbol.name, symbol);
587 }
588
589 pub fn lookup_symbol(&self, name: InternedStr) -> Option<&Symbol> {
591 let mut scope_id = Some(self.current_scope);
592 while let Some(id) = scope_id {
593 let scope = &self.scopes[id.0];
594 if let Some(sym) = scope.symbols.get(&name) {
595 return Some(sym);
596 }
597 scope_id = scope.parent;
598 }
599 None
600 }
601
602 pub fn begin_param_inference(&mut self, params: &[InternedStr]) {
608 self.constraint_mode = true;
609 self.type_vars.clear();
610 self.constraints.clear();
611 self.next_type_var = 0;
612
613 for ¶m in params {
614 let var = TypeVar(self.next_type_var);
615 self.next_type_var += 1;
616 self.type_vars.insert(param, var);
617 }
618 }
619
620 pub fn end_param_inference(&mut self) -> HashMap<InternedStr, Type> {
622 self.constraint_mode = false;
623 let solutions = self.solve_constraints();
624
625 let mut result = HashMap::new();
627 for (&name, &var) in &self.type_vars {
628 if let Some(ty) = solutions.get(&var) {
629 result.insert(name, ty.clone());
630 }
631 }
632
633 self.type_vars.clear();
635 self.constraints.clear();
636 self.next_type_var = 0;
637
638 result
639 }
640
641 fn solve_constraints(&self) -> HashMap<TypeVar, Type> {
643 let mut solutions = HashMap::new();
644
645 for constraint in &self.constraints {
646 match constraint {
647 TypeConstraint::FunctionArg { var, func_name, arg_index } => {
648 if solutions.contains_key(var) {
649 continue;
650 }
651
652 if let Some(ty) = self.lookup_rust_decl_param_type(*func_name, *arg_index) {
654 solutions.insert(*var, ty);
655 }
656 }
657 TypeConstraint::HasField { var, field } => {
658 if solutions.contains_key(var) {
659 continue;
660 }
661
662 if let Some(fields_dict) = self.fields_dict {
664 if let Some(struct_name) = fields_dict.lookup_unique(*field) {
665 solutions.insert(
667 *var,
668 Type::Pointer(
669 Box::new(Type::TypedefName(struct_name)),
670 TypeQualifiers::default(),
671 ),
672 );
673 }
674 }
675 }
676 }
677 }
678
679 solutions
680 }
681
682 fn lookup_rust_decl_param_type(&self, func_name: InternedStr, arg_index: usize) -> Option<Type> {
684 let rust_decl_dict = self.rust_decl_dict?;
685 let func_name_str = self.interner.get(func_name);
686 let rust_fn = rust_decl_dict.fns.get(func_name_str)?;
687 let param = rust_fn.params.get(arg_index)?;
688 Some(self.parse_rust_type_string(¶m.ty))
690 }
691
692 fn lookup_inline_fn_param_type_repr(
698 &self,
699 func_name: InternedStr,
700 arg_index: usize,
701 ) -> Option<TypeRepr> {
702 let dict = self.inline_fn_dict?;
703 let func_def = dict.get(func_name)?;
704
705 let param_list = func_def.declarator.derived.iter()
706 .find_map(|d| match d {
707 DerivedDecl::Function(params) => Some(params),
708 _ => None,
709 })?;
710
711 let param = param_list.params.get(arg_index)?;
712
713 let specs = CTypeSpecs::from_decl_specs(¶m.specs, self.interner);
714 let derived = param.declarator.as_ref()
715 .map(|d| {
716 CDerivedType::from_derived_decls_with_base_const(
718 &d.derived, param.specs.qualifiers.is_const)
719 .into_iter()
720 .take_while(|d| !matches!(d, CDerivedType::Function { .. }))
721 .collect()
722 })
723 .unwrap_or_default();
724
725 Some(TypeRepr::CType {
726 specs,
727 derived,
728 source: CTypeSource::InlineFn { func_name },
729 })
730 }
731
732 fn lookup_inline_fn_return_type_repr(&self, func_name: InternedStr) -> Option<TypeRepr> {
734 let dict = self.inline_fn_dict?;
735 let func_def = dict.get(func_name)?;
736
737 let specs = CTypeSpecs::from_decl_specs(&func_def.specs, self.interner);
738
739 let derived: Vec<_> = CDerivedType::from_derived_decls_with_base_const(
741 &func_def.declarator.derived, func_def.specs.qualifiers.is_const)
742 .into_iter()
743 .take_while(|d| !matches!(d, CDerivedType::Function { .. }))
744 .collect();
745
746 Some(TypeRepr::CType {
747 specs,
748 derived,
749 source: CTypeSource::InlineFn { func_name },
750 })
751 }
752
753 pub fn resolve_decl_specs(&mut self, specs: &DeclSpecs) -> Type {
755 let mut is_signed = false;
757 let mut is_unsigned = false;
758 let mut is_short = false;
759 let mut is_long = 0u8; let mut base_type: Option<Type> = None;
761
762 for spec in &specs.type_specs {
763 match spec {
764 TypeSpec::Void => base_type = Some(Type::Void),
765 TypeSpec::Char => base_type = Some(Type::Char),
766 TypeSpec::Short => is_short = true,
767 TypeSpec::Int => {
768 if base_type.is_none() {
769 base_type = Some(Type::Int);
770 }
771 }
772 TypeSpec::Long => is_long += 1,
773 TypeSpec::Float => base_type = Some(Type::Float),
774 TypeSpec::Double => base_type = Some(Type::Double),
775 TypeSpec::Signed => is_signed = true,
776 TypeSpec::Unsigned => is_unsigned = true,
777 TypeSpec::Bool => base_type = Some(Type::Bool),
778 TypeSpec::Int128 => base_type = Some(Type::Int128),
779 TypeSpec::Struct(s) => {
780 let members = self.resolve_struct_members(s);
781 if let (Some(name), Some(m)) = (s.name, &members) {
782 self.struct_defs.insert(name, m.clone());
783 }
784 base_type = Some(Type::Struct {
785 name: s.name,
786 members,
787 });
788 }
789 TypeSpec::Union(s) => {
790 let members = self.resolve_struct_members(s);
791 if let (Some(name), Some(m)) = (s.name, &members) {
792 self.union_defs.insert(name, m.clone());
793 }
794 base_type = Some(Type::Union {
795 name: s.name,
796 members,
797 });
798 }
799 TypeSpec::Enum(e) => {
800 self.process_enum(e);
802 base_type = Some(Type::Enum { name: e.name });
803 }
804 TypeSpec::TypedefName(name) => {
805 if let Some(ty) = self.typedef_defs.get(name) {
807 base_type = Some(ty.clone());
808 } else {
809 base_type = Some(Type::TypedefName(*name));
810 }
811 }
812 TypeSpec::TypeofExpr(_) => {
813 base_type = Some(Type::Unknown);
815 }
816 _ => {}
817 }
818 }
819
820 match (is_unsigned, is_signed, is_short, is_long, &base_type) {
822 (true, _, _, 0, None) | (true, _, _, 0, Some(Type::Int)) => Type::UnsignedInt,
824 (true, _, _, 1, _) => Type::UnsignedLong,
825 (true, _, _, 2, _) => Type::UnsignedLongLong,
826 (true, _, true, _, _) => Type::UnsignedShort,
827 (true, _, _, _, Some(Type::Char)) => Type::UnsignedChar,
828 (true, _, _, _, Some(Type::Int128)) => Type::UnsignedInt128,
829 (_, true, _, _, Some(Type::Char)) => Type::SignedChar,
831 (_, _, _, 1, None) | (_, _, _, 1, Some(Type::Int)) => Type::Long,
833 (_, _, _, 2, _) => Type::LongLong,
834 (_, _, _, 1, Some(Type::Double)) => Type::LongDouble,
835 (_, _, true, _, _) => Type::Short,
837 _ => base_type.unwrap_or(Type::Int),
839 }
840 }
841
842 fn resolve_struct_members(&mut self, spec: &StructSpec) -> Option<Vec<(InternedStr, Type)>> {
844 spec.members.as_ref().map(|members| {
845 let mut result = Vec::new();
846 for member in members {
847 let base_ty = self.resolve_decl_specs(&member.specs);
848 for decl in &member.declarators {
849 if let Some(ref d) = decl.declarator {
850 if let Some(name) = d.name {
851 let ty = self.apply_declarator(&base_ty, d);
852 result.push((name, ty));
853 }
854 }
855 }
856 }
857 result
858 })
859 }
860
861 fn process_enum(&mut self, spec: &EnumSpec) {
863 if let Some(ref enumerators) = spec.enumerators {
864 let mut value = 0i64;
865 for e in enumerators {
866 if e.value.is_some() {
867 value = 0; }
870 self.define_symbol(Symbol {
871 name: e.name,
872 ty: Type::Int,
873 loc: spec.loc.clone(),
874 kind: SymbolKind::EnumConstant(value),
875 });
876 value += 1;
877 }
878 }
879 }
880
881 pub fn apply_declarator(&self, base_type: &Type, decl: &Declarator) -> Type {
883 let mut ty = base_type.clone();
884
885 for derived in &decl.derived {
886 ty = match derived {
887 DerivedDecl::Pointer(quals) => Type::Pointer(Box::new(ty), quals.clone()),
888 DerivedDecl::Array(arr) => {
889 let _size = &arr.size;
891 Type::Array(Box::new(ty), None)
892 }
893 DerivedDecl::Function(params) => {
894 let param_types: Vec<_> = params.params
895 .iter()
896 .map(|p| {
897 let base = self.resolve_decl_specs_readonly(&p.specs);
898 if let Some(ref d) = p.declarator {
899 self.apply_declarator(&base, d)
900 } else {
901 base
902 }
903 })
904 .collect();
905 Type::Function {
906 return_type: Box::new(ty),
907 params: param_types,
908 variadic: params.is_variadic,
909 }
910 }
911 };
912 }
913
914 ty
915 }
916
917 fn resolve_decl_specs_readonly(&self, specs: &DeclSpecs) -> Type {
919 let mut is_unsigned = false;
921 let mut is_long = 0u8;
922 let mut base_type: Option<Type> = None;
923
924 for spec in &specs.type_specs {
925 match spec {
926 TypeSpec::Void => base_type = Some(Type::Void),
927 TypeSpec::Char => base_type = Some(Type::Char),
928 TypeSpec::Int => base_type = Some(Type::Int),
929 TypeSpec::Long => is_long += 1,
930 TypeSpec::Float => base_type = Some(Type::Float),
931 TypeSpec::Double => base_type = Some(Type::Double),
932 TypeSpec::Unsigned => is_unsigned = true,
933 TypeSpec::Bool => base_type = Some(Type::Bool),
934 TypeSpec::TypedefName(name) => {
935 if let Some(ty) = self.typedef_defs.get(name) {
936 base_type = Some(ty.clone());
937 } else {
938 base_type = Some(Type::TypedefName(*name));
939 }
940 }
941 _ => {}
942 }
943 }
944
945 match (is_unsigned, is_long, &base_type) {
946 (true, 0, None) | (true, 0, Some(Type::Int)) => Type::UnsignedInt,
947 (true, 1, _) => Type::UnsignedLong,
948 (_, 1, None) | (_, 1, Some(Type::Int)) => Type::Long,
949 (_, 2, _) => Type::LongLong,
950 _ => base_type.unwrap_or(Type::Int),
951 }
952 }
953
954 pub fn resolve_type_name(&self, type_name: &TypeName) -> Type {
956 let base_ty = self.resolve_decl_specs_readonly(&type_name.specs);
957 if let Some(ref abs_decl) = type_name.declarator {
958 self.apply_abstract_declarator(&base_ty, abs_decl)
959 } else {
960 base_ty
961 }
962 }
963
964 fn apply_abstract_declarator(&self, base_type: &Type, decl: &AbstractDeclarator) -> Type {
966 let mut ty = base_type.clone();
967
968 for derived in &decl.derived {
969 ty = match derived {
970 DerivedDecl::Pointer(quals) => Type::Pointer(Box::new(ty), quals.clone()),
971 DerivedDecl::Array(_) => {
972 Type::Array(Box::new(ty), None)
973 }
974 DerivedDecl::Function(params) => {
975 let param_types: Vec<_> = params.params
976 .iter()
977 .map(|p| {
978 let base = self.resolve_decl_specs_readonly(&p.specs);
979 if let Some(ref d) = p.declarator {
980 self.apply_declarator(&base, d)
981 } else {
982 base
983 }
984 })
985 .collect();
986 Type::Function {
987 return_type: Box::new(ty),
988 params: param_types,
989 variadic: params.is_variadic,
990 }
991 }
992 };
993 }
994
995 ty
996 }
997
998 pub fn process_declaration(&mut self, decl: &Declaration) {
1000 let base_ty = self.resolve_decl_specs(&decl.specs);
1001
1002 if decl.specs.storage == Some(StorageClass::Typedef) {
1004 for init_decl in &decl.declarators {
1005 if let Some(name) = init_decl.declarator.name {
1006 let ty = self.apply_declarator(&base_ty, &init_decl.declarator);
1007 self.typedef_defs.insert(name, ty);
1008 }
1009 }
1010 return;
1011 }
1012
1013 for init_decl in &decl.declarators {
1015 if let Some(name) = init_decl.declarator.name {
1016 let ty = self.apply_declarator(&base_ty, &init_decl.declarator);
1017 self.define_symbol(Symbol {
1018 name,
1019 ty,
1020 loc: decl.loc().clone(),
1021 kind: SymbolKind::Variable,
1022 });
1023 }
1024 }
1025 }
1026
1027 pub fn process_function_def(&mut self, func: &FunctionDef) {
1029 let return_ty = self.resolve_decl_specs(&func.specs);
1030 let func_ty = self.apply_declarator(&return_ty, &func.declarator);
1031
1032 if let Some(name) = func.declarator.name {
1034 self.define_symbol(Symbol {
1035 name,
1036 ty: func_ty.clone(),
1037 loc: func.loc().clone(),
1038 kind: SymbolKind::Function,
1039 });
1040 }
1041
1042 self.push_scope();
1044
1045 if let Type::Function { params, .. } = &func_ty {
1047 for derived in &func.declarator.derived {
1048 if let DerivedDecl::Function(param_list) = derived {
1049 for (param, param_ty) in param_list.params.iter().zip(params.iter()) {
1050 if let Some(ref decl) = param.declarator {
1051 if let Some(name) = decl.name {
1052 self.define_symbol(Symbol {
1053 name,
1054 ty: param_ty.clone(),
1055 loc: func.loc().clone(),
1056 kind: SymbolKind::Variable,
1057 });
1058 }
1059 }
1060 }
1061 break;
1062 }
1063 }
1064 }
1065
1066 self.process_compound_stmt(&func.body);
1068
1069 self.pop_scope();
1070 }
1071
1072 pub fn process_compound_stmt(&mut self, stmt: &CompoundStmt) {
1074 self.push_scope();
1075 for item in &stmt.items {
1076 match item {
1077 BlockItem::Decl(decl) => self.process_declaration(decl),
1078 BlockItem::Stmt(stmt) => self.process_stmt(stmt),
1079 }
1080 }
1081 self.pop_scope();
1082 }
1083
1084 fn process_stmt(&mut self, stmt: &Stmt) {
1086 match stmt {
1087 Stmt::Compound(compound) => self.process_compound_stmt(compound),
1088 Stmt::For { init, .. } => {
1089 self.push_scope();
1090 if let Some(ForInit::Decl(decl)) = init {
1091 self.process_declaration(decl);
1092 }
1093 self.pop_scope();
1095 }
1096 _ => {}
1097 }
1098 }
1099
1100 pub fn set_macro_params(&mut self, params: &[InternedStr]) {
1106 self.macro_params.clear();
1107 for ¶m in params {
1108 self.macro_params.insert(param);
1109 }
1110 }
1111
1112 pub fn clear_macro_params(&mut self) {
1114 self.macro_params.clear();
1115 }
1116
1117 pub fn register_macro_params_from_apidoc(
1125 &mut self,
1126 macro_name: InternedStr,
1127 params: &[InternedStr],
1128 files: &'a FileRegistry,
1129 typedefs: &'a HashSet<InternedStr>,
1130 ) {
1131 self.files = Some(files);
1133 self.parser_typedefs = Some(typedefs);
1134
1135 self.macro_params.clear();
1137 for ¶m in params {
1138 self.macro_params.insert(param);
1139 }
1140
1141 let macro_name_str = self.interner.get(macro_name);
1143 if let Some(apidoc) = self.apidoc {
1144 if let Some(entry) = apidoc.get(macro_name_str) {
1145 for (i, ¶m_name) in params.iter().enumerate() {
1147 if let Some(apidoc_arg) = entry.args.get(i) {
1148 match parse_type_from_string(
1150 &apidoc_arg.ty,
1151 self.interner,
1152 files,
1153 typedefs,
1154 ) {
1155 Ok(type_name) => {
1156 let ty = self.resolve_type_name(&type_name);
1157 self.define_symbol(Symbol {
1158 name: param_name,
1159 ty,
1160 loc: SourceLocation::default(),
1161 kind: SymbolKind::Variable,
1162 });
1163 }
1164 Err(_) => {}
1165 }
1166 }
1167 }
1168 }
1169 }
1170 }
1171
1172 fn parse_type_string(&self, s: &str) -> TypeRepr {
1177 if let (Some(files), Some(typedefs)) = (self.files, self.parser_typedefs) {
1178 TypeRepr::from_c_type_string(s, self.interner, files, typedefs)
1179 } else {
1180 TypeRepr::from_apidoc_string(s, self.interner)
1181 }
1182 }
1183
1184 fn is_macro_param(&self, name: InternedStr) -> bool {
1186 self.macro_params.contains(&name)
1187 }
1188
1189 fn make_sv_ptr_type(&self) -> TypeRepr {
1191 let sv_name = self.interner.lookup("SV")
1192 .expect("SV should be interned");
1193 TypeRepr::CType {
1194 specs: CTypeSpecs::TypedefName(sv_name),
1195 derived: vec![CDerivedType::Pointer { is_const: false, is_volatile: false, is_restrict: false }],
1196 source: CTypeSource::SvFamilyCast,
1197 }
1198 }
1199
1200 fn make_sv_family_ptr_type(&self, typedef_name: InternedStr) -> TypeRepr {
1202 TypeRepr::CType {
1203 specs: CTypeSpecs::TypedefName(typedef_name),
1204 derived: vec![CDerivedType::Pointer { is_const: false, is_volatile: false, is_restrict: false }],
1205 source: CTypeSource::CommonMacroFieldInference,
1206 }
1207 }
1208
1209 fn try_infer_sv_family_from_member(
1215 &self,
1216 member: InternedStr,
1217 base: &Expr,
1218 type_env: &mut TypeEnv,
1219 ) {
1220 let Some(fields_dict) = self.fields_dict else { return };
1221 let Some(macro_id) = fields_dict.defining_macro_of(member) else { return };
1222 let Some(sv_typedef) = fields_dict.sv_family_of_common_macro(macro_id) else { return };
1223 let Some((_param_name, param_node_id)) = leftmost_param_ident(base, &self.macro_params)
1224 else {
1225 return;
1226 };
1227 let sv_type = self.make_sv_family_ptr_type(sv_typedef);
1228 let typedef_str = self.interner.get(sv_typedef);
1229 let member_str = self.interner.get(member);
1230 type_env.add_constraint(TypeEnvConstraint::new(
1231 param_node_id,
1232 sv_type,
1233 format!("common-macro field {} implies {}*", member_str, typedef_str),
1234 ));
1235 }
1236
1237 fn get_expr_type_repr(&self, expr_id: ExprId, type_env: &TypeEnv) -> Option<TypeRepr> {
1239 type_env.expr_constraints.get(&expr_id)
1240 .and_then(|c| c.first())
1241 .map(|c| c.ty.clone())
1242 }
1243
1244 fn get_expr_type_repr_or_unknown(&self, expr_id: ExprId, type_env: &TypeEnv) -> TypeRepr {
1250 self.get_expr_type_repr(expr_id, type_env)
1251 .unwrap_or_else(|| TypeRepr::from_apidoc_string("<unknown>", self.interner))
1252 }
1253
1254 fn get_expr_type_str(&self, expr_id: ExprId, type_env: &TypeEnv) -> String {
1256 if let Some(constraints) = type_env.expr_constraints.get(&expr_id) {
1257 if let Some(c) = constraints.first() {
1258 return c.ty.to_display_string(self.interner);
1259 }
1260 }
1261 "<unknown>".to_string()
1262 }
1263
1264 fn compute_binary_type_str(&self, op: &BinOp, lhs_id: ExprId, rhs_id: ExprId, type_env: &TypeEnv) -> String {
1266 match op {
1267 BinOp::Lt | BinOp::Gt | BinOp::Le | BinOp::Ge |
1269 BinOp::Eq | BinOp::Ne | BinOp::LogAnd | BinOp::LogOr => "int".to_string(),
1270 _ => {
1272 let lhs_ty = self.get_expr_type_str(lhs_id, type_env);
1273 let rhs_ty = self.get_expr_type_str(rhs_id, type_env);
1274 self.usual_arithmetic_conversion_str(&lhs_ty, &rhs_ty)
1275 }
1276 }
1277 }
1278
1279 fn usual_arithmetic_conversion_str(&self, lhs: &str, rhs: &str) -> String {
1281 let is_ptr = |ty: &str| ty.contains('*');
1283 if is_ptr(lhs) && !is_ptr(rhs) {
1284 return lhs.to_string();
1285 }
1286 if is_ptr(rhs) && !is_ptr(lhs) {
1287 return rhs.to_string();
1288 }
1289
1290 let rank = |ty: &str| -> u8 {
1292 match ty {
1293 "long double" => 10,
1294 "double" => 9,
1295 "float" => 8,
1296 "unsigned long long" => 7,
1297 "long long" => 6,
1298 "unsigned long" => 5,
1299 "long" => 4,
1300 "unsigned int" => 3,
1301 "int" => 2,
1302 "unsigned short" => 1,
1303 "short" => 1,
1304 _ => 0,
1305 }
1306 };
1307
1308 if rank(lhs) >= rank(rhs) {
1309 lhs.to_string()
1310 } else {
1311 rhs.to_string()
1312 }
1313 }
1314
1315 fn compute_conditional_type_str(&self, then_id: ExprId, else_id: ExprId, type_env: &TypeEnv) -> String {
1317 let then_ty = self.get_expr_type_str(then_id, type_env);
1318 let else_ty = self.get_expr_type_str(else_id, type_env);
1319 let is_void_ptr = |s: &str| s.contains("void") && s.contains('*');
1321 let is_concrete_ptr = |s: &str| !s.contains("void") && s.contains('*');
1322 if is_void_ptr(&then_ty) && is_concrete_ptr(&else_ty) {
1323 return else_ty;
1324 }
1325 if is_void_ptr(&else_ty) && is_concrete_ptr(&then_ty) {
1326 return then_ty;
1327 }
1328 self.usual_arithmetic_conversion_str(&then_ty, &else_ty)
1329 }
1330
1331 pub fn collect_stmt_constraints(&mut self, stmt: &Stmt, type_env: &mut TypeEnv) {
1335 match stmt {
1336 Stmt::Compound(compound) => {
1337 for item in &compound.items {
1338 match item {
1339 BlockItem::Stmt(s) => self.collect_stmt_constraints(s, type_env),
1340 BlockItem::Decl(_) => {} }
1342 }
1343 }
1344 Stmt::Expr(Some(expr), _) => {
1345 self.collect_expr_constraints(expr, type_env);
1346 }
1347 Stmt::If { cond, then_stmt, else_stmt, .. } => {
1348 self.collect_expr_constraints(cond, type_env);
1349 self.collect_stmt_constraints(then_stmt, type_env);
1350 if let Some(else_s) = else_stmt {
1351 self.collect_stmt_constraints(else_s, type_env);
1352 }
1353 }
1354 Stmt::While { cond, body, .. } => {
1355 self.collect_expr_constraints(cond, type_env);
1356 self.collect_stmt_constraints(body, type_env);
1357 }
1358 Stmt::DoWhile { body, cond, .. } => {
1359 self.collect_stmt_constraints(body, type_env);
1360 self.collect_expr_constraints(cond, type_env);
1361 }
1362 Stmt::For { init, cond, step, body, .. } => {
1363 if let Some(ForInit::Expr(e)) = init {
1364 self.collect_expr_constraints(e, type_env);
1365 }
1366 if let Some(c) = cond {
1367 self.collect_expr_constraints(c, type_env);
1368 }
1369 if let Some(s) = step {
1370 self.collect_expr_constraints(s, type_env);
1371 }
1372 self.collect_stmt_constraints(body, type_env);
1373 }
1374 Stmt::Return(Some(expr), _) => {
1375 self.collect_expr_constraints(expr, type_env);
1376 }
1377 Stmt::Switch { expr, body, .. } => {
1378 self.collect_expr_constraints(expr, type_env);
1379 self.collect_stmt_constraints(body, type_env);
1380 }
1381 Stmt::Case { expr, stmt, .. } => {
1382 self.collect_expr_constraints(expr, type_env);
1383 self.collect_stmt_constraints(stmt, type_env);
1384 }
1385 Stmt::Default { stmt, .. } | Stmt::Label { stmt, .. } => {
1386 self.collect_stmt_constraints(stmt, type_env);
1387 }
1388 _ => {} }
1390 }
1391
1392 fn resolve_typedef_to_struct_name<'b>(&self, name: &'b str) -> std::borrow::Cow<'b, str> {
1407 let Some(rd) = self.rust_decl_dict else {
1408 return std::borrow::Cow::Borrowed(name);
1409 };
1410 let mut current = std::borrow::Cow::Borrowed(name);
1411 for _ in 0..8 {
1412 if rd.structs.contains_key(current.as_ref()) {
1414 return current;
1415 }
1416 let Some(alias) = rd.types.get(current.as_ref()) else {
1418 return current;
1419 };
1420 current = std::borrow::Cow::Owned(alias.ty.clone());
1421 }
1422 current
1423 }
1424
1425 fn replace_anonymous_with_bindings(
1426 &self,
1427 parent_struct_name_str: &str,
1428 field_name_str: &str,
1429 c_field_type: TypeRepr,
1430 ) -> TypeRepr {
1431 if !is_anonymous_struct_or_union_field(&c_field_type) {
1432 return c_field_type;
1433 }
1434 let Some(rd) = self.rust_decl_dict else {
1435 return c_field_type;
1436 };
1437 let resolved = self.resolve_typedef_to_struct_name(parent_struct_name_str);
1438 let Some(rust_struct) = rd.structs.get(resolved.as_ref()) else {
1439 return c_field_type;
1440 };
1441 let Some(rust_field) = rust_struct.fields.iter().find(|f| f.name == field_name_str) else {
1442 return c_field_type;
1443 };
1444 TypeRepr::from_unified_type(&rust_field.uty, self.interner)
1448 }
1449
1450 fn lookup_field_in_bindings(
1455 &self,
1456 parent_struct_name_str: &str,
1457 field_name_str: &str,
1458 ) -> Option<TypeRepr> {
1459 let rd = self.rust_decl_dict?;
1460 let resolved = self.resolve_typedef_to_struct_name(parent_struct_name_str);
1461 let rust_struct = rd.structs.get(resolved.as_ref())?;
1462 let rust_field = rust_struct.fields.iter().find(|f| f.name == field_name_str)?;
1463 Some(TypeRepr::from_unified_type(&rust_field.uty, self.interner))
1464 }
1465
1466 pub fn collect_expr_constraints(&mut self, expr: &Expr, type_env: &mut TypeEnv) {
1470 match &expr.kind {
1471 ExprKind::IntLit(_) => {
1473 type_env.add_constraint(TypeEnvConstraint::new(
1474 expr.id,
1475 TypeRepr::Inferred(InferredType::IntLiteral),
1476 "integer literal",
1477 ));
1478 }
1479 ExprKind::UIntLit(_) => {
1480 type_env.add_constraint(TypeEnvConstraint::new(
1481 expr.id,
1482 TypeRepr::Inferred(InferredType::UIntLiteral),
1483 "unsigned integer literal",
1484 ));
1485 }
1486 ExprKind::FloatLit(_) => {
1487 type_env.add_constraint(TypeEnvConstraint::new(
1488 expr.id,
1489 TypeRepr::Inferred(InferredType::FloatLiteral),
1490 "float literal",
1491 ));
1492 }
1493 ExprKind::CharLit(_) => {
1494 type_env.add_constraint(TypeEnvConstraint::new(
1495 expr.id,
1496 TypeRepr::Inferred(InferredType::CharLiteral),
1497 "char literal",
1498 ));
1499 }
1500 ExprKind::StringLit(_) => {
1501 type_env.add_constraint(TypeEnvConstraint::new(
1502 expr.id,
1503 TypeRepr::Inferred(InferredType::StringLiteral),
1504 "string literal",
1505 ));
1506 }
1507
1508 ExprKind::Ident(name) => {
1510 let name_str = self.interner.get(*name);
1511
1512 if let Some(sym) = self.lookup_symbol(*name) {
1514 let ty_str = sym.ty.display(self.interner);
1515 let resolved = TypeRepr::from_apidoc_string(&ty_str, self.interner);
1518 type_env.add_constraint(TypeEnvConstraint::new(
1519 expr.id,
1520 TypeRepr::Inferred(InferredType::SymbolLookup {
1521 name: *name,
1522 resolved_type: Box::new(resolved),
1523 }),
1524 "symbol lookup",
1525 ));
1526 } else if let Some(rust_decl_dict) = self.rust_decl_dict {
1528 if let Some(rust_const) = rust_decl_dict.lookup_const(name_str) {
1529 type_env.add_constraint(TypeEnvConstraint::new(
1530 expr.id,
1531 TypeRepr::RustType {
1532 repr: RustTypeRepr::from_type_string(&rust_const.ty),
1533 source: RustTypeSource::Const {
1534 const_name: name_str.to_string(),
1535 },
1536 },
1537 "bindings constant",
1538 ));
1539 } else if name_str == "my_perl" {
1540 type_env.add_constraint(TypeEnvConstraint::new(
1542 expr.id,
1543 TypeRepr::Inferred(InferredType::ThxDefault),
1544 "THX default type",
1545 ));
1546 }
1547 } else if name_str == "my_perl" {
1548 type_env.add_constraint(TypeEnvConstraint::new(
1550 expr.id,
1551 TypeRepr::Inferred(InferredType::ThxDefault),
1552 "THX default type",
1553 ));
1554 }
1555
1556 if self.is_macro_param(*name) {
1558 type_env.link_expr_to_param(expr.id, *name, "parameter reference");
1559 }
1560 }
1561
1562 ExprKind::Call { func, args } => {
1564 self.collect_expr_constraints(func, type_env);
1566 for arg in args {
1567 self.collect_expr_constraints(arg, type_env);
1568 }
1569 self.collect_call_constraints(expr.id, func, args, type_env);
1571 }
1572
1573 ExprKind::Binary { op, lhs, rhs } => {
1575 self.collect_expr_constraints(lhs, type_env);
1577 self.collect_expr_constraints(rhs, type_env);
1578 let result_ty_str = self.compute_binary_type_str(op, lhs.id, rhs.id, type_env);
1580 let result_type = TypeRepr::from_apidoc_string(&result_ty_str, self.interner);
1581 type_env.add_constraint(TypeEnvConstraint::new(
1582 expr.id,
1583 TypeRepr::Inferred(InferredType::BinaryOp {
1584 op: *op,
1585 result_type: Box::new(result_type),
1586 }),
1587 "binary expression",
1588 ));
1589 }
1590
1591 ExprKind::Conditional { cond, then_expr, else_expr } => {
1593 self.collect_expr_constraints(cond, type_env);
1594 self.collect_expr_constraints(then_expr, type_env);
1595 self.collect_expr_constraints(else_expr, type_env);
1596 let then_type = self.get_expr_type_repr_or_unknown(then_expr.id, type_env);
1597 let else_type = self.get_expr_type_repr_or_unknown(else_expr.id, type_env);
1598 let result_ty_str = self.compute_conditional_type_str(then_expr.id, else_expr.id, type_env);
1601 let result_type = TypeRepr::from_apidoc_string(&result_ty_str, self.interner);
1602 type_env.add_constraint(TypeEnvConstraint::new(
1603 expr.id,
1604 TypeRepr::Inferred(InferredType::Conditional {
1605 then_type: Box::new(then_type),
1606 else_type: Box::new(else_type),
1607 result_type: Box::new(result_type),
1608 }),
1609 "conditional expression",
1610 ));
1611 }
1612
1613 ExprKind::Cast { type_name, expr: inner } => {
1615 self.collect_expr_constraints(inner, type_env);
1616 let specs = CTypeSpecs::from_decl_specs(&type_name.specs, self.interner);
1618 let derived: Vec<CDerivedType> = type_name.declarator.as_ref()
1619 .map(|d| {
1620 CDerivedType::from_derived_decls_with_base_const(
1621 &d.derived, type_name.specs.qualifiers.is_const)
1622 .into_iter()
1623 .take_while(|d| !matches!(d, CDerivedType::Function { .. }))
1624 .collect()
1625 })
1626 .unwrap_or_default();
1627 let target_type = TypeRepr::CType {
1628 specs: specs.clone(),
1629 derived: derived.clone(),
1630 source: CTypeSource::Cast,
1631 };
1632 type_env.add_constraint(TypeEnvConstraint::new(
1633 expr.id,
1634 TypeRepr::Inferred(InferredType::Cast {
1635 target_type: Box::new(target_type),
1636 }),
1637 "cast expression",
1638 ));
1639
1640 if let Some(fields_dict) = self.fields_dict {
1643 let is_single_ptr = derived.len() == 1
1644 && matches!(derived[0], CDerivedType::Pointer { .. });
1645 if is_single_ptr {
1646 if let Some(type_name_id) = specs.type_name() {
1647 if fields_dict.is_sv_family_type(type_name_id) {
1648 if let ExprKind::Ident(param_name) = &inner.kind {
1649 if self.is_macro_param(*param_name) {
1650 let sv_type = self.make_sv_ptr_type();
1651 type_env.add_constraint(TypeEnvConstraint::new(
1652 inner.id,
1653 sv_type,
1654 "SV family cast",
1655 ));
1656 }
1657 }
1658 }
1659 }
1660 }
1661 }
1662 }
1663
1664 ExprKind::Index { expr: base, index } => {
1666 self.collect_expr_constraints(base, type_env);
1667 self.collect_expr_constraints(index, type_env);
1668 let base_ty_str = self.get_expr_type_str(base.id, type_env);
1670 let elem_ty_str = if base_ty_str.ends_with('*') {
1671 base_ty_str.trim_end_matches('*').trim().to_string()
1672 } else if base_ty_str.contains('[') {
1673 base_ty_str.split('[').next().unwrap_or(&base_ty_str).trim().to_string()
1674 } else {
1675 "<unknown>".to_string()
1676 };
1677 let base_type = TypeRepr::from_apidoc_string(&base_ty_str, self.interner);
1678 let element_type = TypeRepr::from_apidoc_string(&elem_ty_str, self.interner);
1679 type_env.add_constraint(TypeEnvConstraint::new(
1680 expr.id,
1681 TypeRepr::Inferred(InferredType::ArraySubscript {
1682 base_type: Box::new(base_type),
1683 element_type: Box::new(element_type),
1684 }),
1685 "array subscript",
1686 ));
1687 }
1688
1689 ExprKind::Member { expr: base, member } => {
1691 self.collect_expr_constraints(base, type_env);
1692
1693 let base_ty = self.get_expr_type_str(base.id, type_env);
1694 let member_name = self.interner.get(*member);
1695
1696 let field_type = if self.is_sv_u_access(base) {
1700 self.lookup_sv_u_field_type(*member)
1702 .map(|c_type| Box::new(TypeRepr::from_apidoc_string(&c_type, self.interner)))
1703 } else {
1704 let base_type_repr = self.get_expr_type_repr(base.id, type_env);
1706 let struct_name_id = base_type_repr.as_ref().and_then(|t| t.type_name());
1707 let struct_name_str = base_type_repr.as_ref()
1709 .and_then(|t| extract_struct_name_str(t, self.interner));
1710 let field_str = self.interner.get(*member);
1711 let flex_ptr = struct_name_id.and_then(|n| {
1713 self.fields_dict?
1714 .flexible_array_element(n, *member)
1715 .map(|elem| Box::new(wrap_with_outer_pointer(elem.clone(), false)))
1716 });
1717 let direct = flex_ptr.or_else(|| {
1719 struct_name_id.and_then(|n| {
1720 self.fields_dict?.get_field_type(n, *member).map(|ft| {
1721 let parent_str = self.interner.get(n);
1722 let patched = self.replace_anonymous_with_bindings(
1723 parent_str, field_str, ft.type_repr.clone(),
1724 );
1725 Box::new(patched)
1726 })
1727 })
1728 });
1729 let direct = direct.or_else(|| {
1732 struct_name_str.as_deref().and_then(|parent_str| {
1733 self.lookup_field_in_bindings(parent_str, field_str)
1734 .map(Box::new)
1735 })
1736 });
1737 direct.or_else(|| {
1740 self.fields_dict
1741 .and_then(|fd| fd.rust_type_of_common_field(*member))
1742 .cloned()
1743 .map(Box::new)
1744 })
1745 };
1746
1747 type_env.add_constraint(TypeEnvConstraint::new(
1748 expr.id,
1749 TypeRepr::Inferred(InferredType::MemberAccess {
1750 base_type: base_ty.clone(),
1751 member: *member,
1752 field_type,
1753 }),
1754 format!("{}.{}", base_ty, member_name),
1755 ));
1756
1757 self.try_infer_sv_family_from_member(*member, base, type_env);
1759 }
1760
1761 ExprKind::PtrMember { expr: base, member } => {
1763 self.collect_expr_constraints(base, type_env);
1764
1765 let base_ty = self.get_expr_type_str(base.id, type_env);
1767 let member_name = self.interner.get(*member);
1768
1769 if let Some(fields_dict) = self.fields_dict {
1772 if base_ty == "/* unknown */" || self.is_ident_expr(base) {
1774 let inferred_struct = fields_dict.lookup_unique(*member)
1777 .or_else(|| fields_dict.get_consistent_base_type(*member, self.interner));
1778
1779 if let Some(struct_name) = inferred_struct {
1780 let type_name = fields_dict.get_typedef_for_struct(struct_name)
1782 .unwrap_or(struct_name);
1783 let type_name_str = self.interner.get(type_name);
1784 let base_type = TypeRepr::CType {
1785 specs: CTypeSpecs::TypedefName(type_name),
1786 derived: vec![CDerivedType::Pointer {
1787 is_const: false,
1788 is_volatile: false,
1789 is_restrict: false,
1790 }],
1791 source: CTypeSource::FieldInference { field_name: *member },
1792 };
1793 type_env.add_constraint(TypeEnvConstraint::new(
1794 base.id,
1795 base_type,
1796 format!("field {} implies {}*", member_name, type_name_str),
1797 ));
1798 }
1799 }
1800 }
1801
1802 let base_type_repr = self.get_expr_type_repr(base.id, type_env);
1804 let pointee = base_type_repr.as_ref().and_then(|t| t.pointee_name());
1805 let (field_type, used_consistent_type) = if let Some(name) = pointee {
1806 let flex_ptr = self.fields_dict.and_then(|fd| {
1808 fd.flexible_array_element(name, *member)
1809 .map(|elem| Box::new(wrap_with_outer_pointer(elem.clone(), false)))
1810 });
1811 let parent_str = self.interner.get(name);
1812 let field_str = self.interner.get(*member);
1813 let direct = flex_ptr.or_else(|| {
1816 self.fields_dict
1817 .and_then(|fd| fd.get_field_type(name, *member))
1818 .map(|ft| {
1819 let patched = self.replace_anonymous_with_bindings(
1820 parent_str, field_str, ft.type_repr.clone(),
1821 );
1822 Box::new(patched)
1823 })
1824 });
1825 let direct = direct.or_else(|| {
1828 self.lookup_field_in_bindings(parent_str, field_str)
1829 .map(Box::new)
1830 });
1831 let ty = direct.or_else(|| {
1832 self.fields_dict
1834 .and_then(|fd| fd.rust_type_of_common_field(*member))
1835 .cloned()
1836 .map(Box::new)
1837 });
1838 (ty, false)
1839 } else if let Some(fields_dict) = self.fields_dict {
1840 let consistent = fields_dict.get_consistent_field_type(*member)
1842 .cloned()
1843 .map(Box::new);
1844 let ty = consistent.or_else(|| {
1845 fields_dict.rust_type_of_common_field(*member)
1846 .cloned()
1847 .map(Box::new)
1848 });
1849 (ty, true)
1850 } else {
1851 (None, false)
1852 };
1853
1854 type_env.add_constraint(TypeEnvConstraint::new(
1855 expr.id,
1856 TypeRepr::Inferred(InferredType::PtrMemberAccess {
1857 base_type: base_ty.clone(),
1858 member: *member,
1859 field_type,
1860 used_consistent_type,
1861 }),
1862 format!("{}->{}", base_ty, member_name),
1863 ));
1864
1865 self.try_infer_sv_family_from_member(*member, base, type_env);
1867 }
1868
1869 ExprKind::Assign { lhs, rhs, .. } => {
1871 self.collect_expr_constraints(lhs, type_env);
1872 self.collect_expr_constraints(rhs, type_env);
1873 let lhs_type = self.get_expr_type_repr_or_unknown(lhs.id, type_env);
1875 type_env.add_constraint(TypeEnvConstraint::new(
1876 expr.id,
1877 TypeRepr::Inferred(InferredType::Assignment {
1878 lhs_type: Box::new(lhs_type),
1879 }),
1880 "assignment expression",
1881 ));
1882 }
1883
1884 ExprKind::Comma { lhs, rhs } => {
1886 self.collect_expr_constraints(lhs, type_env);
1887 self.collect_expr_constraints(rhs, type_env);
1888 let rhs_type = self.get_expr_type_repr_or_unknown(rhs.id, type_env);
1890 type_env.add_constraint(TypeEnvConstraint::new(
1891 expr.id,
1892 TypeRepr::Inferred(InferredType::Comma {
1893 rhs_type: Box::new(rhs_type),
1894 }),
1895 "comma expression",
1896 ));
1897 }
1898
1899 ExprKind::PreInc(inner) | ExprKind::PreDec(inner) |
1901 ExprKind::PostInc(inner) | ExprKind::PostDec(inner) => {
1902 self.collect_expr_constraints(inner, type_env);
1903 let inner_type = self.get_expr_type_repr_or_unknown(inner.id, type_env);
1904 type_env.add_constraint(TypeEnvConstraint::new(
1905 expr.id,
1906 TypeRepr::Inferred(InferredType::IncDec {
1907 inner_type: Box::new(inner_type),
1908 }),
1909 "increment/decrement",
1910 ));
1911 }
1912
1913 ExprKind::AddrOf(inner) => {
1915 self.collect_expr_constraints(inner, type_env);
1916 let inner_type = self.get_expr_type_repr_or_unknown(inner.id, type_env);
1917 type_env.add_constraint(TypeEnvConstraint::new(
1918 expr.id,
1919 TypeRepr::Inferred(InferredType::AddressOf {
1920 inner_type: Box::new(inner_type),
1921 }),
1922 "address-of",
1923 ));
1924 }
1925
1926 ExprKind::Deref(inner) => {
1928 self.collect_expr_constraints(inner, type_env);
1929 let pointer_type = self.get_expr_type_repr_or_unknown(inner.id, type_env);
1930 type_env.add_constraint(TypeEnvConstraint::new(
1931 expr.id,
1932 TypeRepr::Inferred(InferredType::Dereference {
1933 pointer_type: Box::new(pointer_type),
1934 }),
1935 "dereference",
1936 ));
1937 }
1938
1939 ExprKind::UnaryPlus(inner) | ExprKind::UnaryMinus(inner) => {
1941 self.collect_expr_constraints(inner, type_env);
1942 let inner_type = self.get_expr_type_repr_or_unknown(inner.id, type_env);
1943 type_env.add_constraint(TypeEnvConstraint::new(
1944 expr.id,
1945 TypeRepr::Inferred(InferredType::UnaryArithmetic {
1946 inner_type: Box::new(inner_type),
1947 }),
1948 "unary plus/minus",
1949 ));
1950 }
1951
1952 ExprKind::BitNot(inner) => {
1954 self.collect_expr_constraints(inner, type_env);
1955 let inner_type = self.get_expr_type_repr_or_unknown(inner.id, type_env);
1956 type_env.add_constraint(TypeEnvConstraint::new(
1957 expr.id,
1958 TypeRepr::Inferred(InferredType::UnaryArithmetic {
1959 inner_type: Box::new(inner_type),
1960 }),
1961 "bitwise not",
1962 ));
1963 }
1964
1965 ExprKind::LogNot(inner) => {
1967 self.collect_expr_constraints(inner, type_env);
1968 type_env.add_constraint(TypeEnvConstraint::new(
1969 expr.id,
1970 TypeRepr::Inferred(InferredType::LogicalNot),
1971 "logical not",
1972 ));
1973 }
1974
1975 ExprKind::Sizeof(inner) => {
1977 self.collect_expr_constraints(inner, type_env);
1978 type_env.add_constraint(TypeEnvConstraint::new(
1979 expr.id,
1980 TypeRepr::Inferred(InferredType::Sizeof),
1981 "sizeof expression",
1982 ));
1983 }
1984
1985 ExprKind::SizeofType(_) => {
1987 type_env.add_constraint(TypeEnvConstraint::new(
1988 expr.id,
1989 TypeRepr::Inferred(InferredType::Sizeof),
1990 "sizeof type",
1991 ));
1992 }
1993
1994 ExprKind::Alignof(_) => {
1996 type_env.add_constraint(TypeEnvConstraint::new(
1997 expr.id,
1998 TypeRepr::Inferred(InferredType::Alignof),
1999 "alignof",
2000 ));
2001 }
2002
2003 ExprKind::CompoundLit { type_name, .. } => {
2005 let ty = self.resolve_type_name(type_name);
2006 let ty_str = ty.display(self.interner);
2007 let type_name_repr = TypeRepr::from_apidoc_string(&ty_str, self.interner);
2008 type_env.add_constraint(TypeEnvConstraint::new(
2009 expr.id,
2010 TypeRepr::Inferred(InferredType::CompoundLiteral {
2011 type_name: Box::new(type_name_repr),
2012 }),
2013 "compound literal",
2014 ));
2015 }
2016
2017 ExprKind::StmtExpr(compound) => {
2019 self.collect_compound_constraints(compound, type_env);
2020 if let Some(last_expr_id) = self.get_last_expr_id(compound) {
2022 let last_expr_type = self.get_expr_type_repr_or_unknown(last_expr_id, type_env);
2023 type_env.add_constraint(TypeEnvConstraint::new(
2024 expr.id,
2025 TypeRepr::Inferred(InferredType::StmtExpr {
2026 last_expr_type: Some(Box::new(last_expr_type)),
2027 }),
2028 "statement expression",
2029 ));
2030 } else {
2031 type_env.add_constraint(TypeEnvConstraint::new(
2032 expr.id,
2033 TypeRepr::Inferred(InferredType::StmtExpr {
2034 last_expr_type: None,
2035 }),
2036 "statement expression (empty)",
2037 ));
2038 }
2039 }
2040
2041 ExprKind::Assert { condition, .. } => {
2043 self.collect_expr_constraints(condition, type_env);
2044 type_env.add_constraint(TypeEnvConstraint::new(
2045 expr.id,
2046 TypeRepr::Inferred(InferredType::Assert),
2047 "assertion",
2048 ));
2049 }
2050
2051 ExprKind::BuiltinCall { name, args } => {
2053 for arg in args {
2055 if let crate::ast::BuiltinArg::Expr(e) = arg {
2056 self.collect_expr_constraints(e, type_env);
2057 }
2058 }
2059 let func_name = self.interner.get(*name);
2061 if func_name == "offsetof" || func_name == "__builtin_offsetof"
2062 || func_name == "STRUCT_OFFSET"
2063 {
2064 type_env.add_constraint(TypeEnvConstraint::new(
2065 expr.id,
2066 TypeRepr::Inferred(InferredType::Sizeof),
2067 "offsetof returns size_t",
2068 ));
2069 }
2070 }
2071
2072 ExprKind::MacroCall { name, args, expanded, .. } => {
2074 for arg in args {
2076 self.collect_expr_constraints(arg, type_env);
2077 }
2078
2079 let macro_name_str = self.interner.get(*name);
2081 if let Some(param_types) = self.get_macro_param_types(macro_name_str) {
2082 for (i, arg) in args.iter().enumerate() {
2083 if let Some((param_name, type_str)) = param_types.get(i) {
2084 let constraint = TypeEnvConstraint::new(
2088 arg.id,
2089 TypeRepr::from_rust_string_propagated(type_str),
2090 format!("arg {} ({}) of macro {}()", i, param_name, macro_name_str),
2091 );
2092 type_env.add_constraint(constraint);
2093 }
2094 }
2095 }
2096
2097 self.collect_expr_constraints(expanded, type_env);
2099 if let Some(constraints) = type_env.get_expr_constraints(expanded.id) {
2101 if let Some(constraint) = constraints.first() {
2102 type_env.add_constraint(TypeEnvConstraint::new(
2103 expr.id,
2104 constraint.ty.clone(),
2105 "macro call (expanded)",
2106 ));
2107 }
2108 }
2109 }
2110 }
2111 }
2112
2113 fn get_last_expr_id(&self, compound: &CompoundStmt) -> Option<ExprId> {
2115 if let Some(BlockItem::Stmt(Stmt::Expr(Some(expr), _))) = compound.items.last() {
2116 Some(expr.id)
2117 } else {
2118 None
2119 }
2120 }
2121
2122 fn is_sv_u_access(&self, base: &Expr) -> bool {
2126 if let ExprKind::PtrMember { member, .. } = &base.kind {
2127 let sv_u_id = self.interner.lookup("sv_u");
2128 sv_u_id.map_or(false, |id| *member == id)
2129 } else {
2130 false
2131 }
2132 }
2133
2134 fn is_ident_expr(&self, expr: &Expr) -> bool {
2138 matches!(expr.kind, ExprKind::Ident(_))
2139 }
2140
2141 fn lookup_sv_u_field_type(&self, field: InternedStr) -> Option<String> {
2146 self.fields_dict?
2147 .get_sv_u_field_type(field)
2148 .map(|s| s.to_string())
2149 }
2150
2151 fn collect_call_constraints(
2153 &mut self,
2154 call_expr_id: ExprId,
2155 func: &Expr,
2156 args: &[Expr],
2157 type_env: &mut TypeEnv,
2158 ) {
2159 let func_name = match &func.kind {
2161 ExprKind::Ident(name) => *name,
2162 _ => return, };
2164
2165 let func_name_str = self.interner.get(func_name);
2166
2167 if let Some(rust_decl_dict) = self.rust_decl_dict {
2169 if let Some(rust_fn) = rust_decl_dict.fns.get(func_name_str) {
2170 for (i, arg) in args.iter().enumerate() {
2171 if let Some(param) = rust_fn.params.get(i) {
2172 let constraint = TypeEnvConstraint::new(
2173 arg.id,
2174 TypeRepr::RustType {
2175 repr: RustTypeRepr::from_type_string(¶m.ty),
2176 source: RustTypeSource::FnParam {
2177 func_name: func_name_str.to_string(),
2178 param_index: i,
2179 },
2180 },
2181 format!("arg {} of {}()", i, func_name_str),
2182 );
2183 type_env.add_constraint(constraint);
2184 }
2185 }
2186
2187 if let Some(ref ret_ty) = rust_fn.ret_ty {
2189 let return_constraint = TypeEnvConstraint::new(
2190 call_expr_id,
2191 TypeRepr::RustType {
2192 repr: RustTypeRepr::from_type_string(ret_ty),
2193 source: RustTypeSource::FnReturn {
2194 func_name: func_name_str.to_string(),
2195 },
2196 },
2197 format!("return type of {}()", func_name_str),
2198 );
2199 type_env.add_constraint(return_constraint);
2200 }
2201 }
2202 }
2203
2204 if let Some(apidoc) = self.apidoc {
2206 if let Some(entry) = apidoc.get(func_name_str) {
2207 for (i, arg) in args.iter().enumerate() {
2209 if let Some(apidoc_arg) = entry.args.get(i) {
2210 let constraint = TypeEnvConstraint::new(
2211 arg.id,
2212 self.parse_type_string(&apidoc_arg.ty),
2213 format!("arg {} ({}) of {}()", i, apidoc_arg.name, func_name_str),
2214 );
2215 type_env.add_constraint(constraint);
2216 }
2217 }
2218
2219 if let Some(ref return_type) = entry.return_type {
2221 let return_constraint = TypeEnvConstraint::new(
2222 call_expr_id,
2223 self.parse_type_string(return_type),
2224 format!("return type of {}()", func_name_str),
2225 );
2226 type_env.add_constraint(return_constraint);
2227 }
2228 }
2229 }
2230
2231 if self.inline_fn_dict.is_some() {
2233 for (i, arg) in args.iter().enumerate() {
2235 if let Some(type_repr) = self.lookup_inline_fn_param_type_repr(func_name, i) {
2236 let constraint = TypeEnvConstraint::new(
2237 arg.id,
2238 type_repr,
2239 format!("arg {} of inline {}()", i, func_name_str),
2240 );
2241 type_env.add_constraint(constraint);
2242 }
2243 }
2244
2245 if let Some(type_repr) = self.lookup_inline_fn_return_type_repr(func_name) {
2247 let return_constraint = TypeEnvConstraint::new(
2248 call_expr_id,
2249 type_repr,
2250 format!("return type of inline {}()", func_name_str),
2251 );
2252 type_env.add_constraint(return_constraint);
2253 }
2254 }
2255
2256 if let Some(param_types) = self.get_macro_param_types(func_name_str) {
2258 for (i, arg) in args.iter().enumerate() {
2259 if let Some((param_name, type_str)) = param_types.get(i) {
2260 let constraint = TypeEnvConstraint::new(
2264 arg.id,
2265 TypeRepr::from_rust_string_propagated(type_str),
2266 format!("arg {} ({}) of macro {}()", i, param_name, func_name_str),
2267 );
2268 type_env.add_constraint(constraint);
2269 }
2270 }
2271 }
2272
2273 if let Some(return_type_str) = self.get_macro_return_type(func_name_str) {
2275 let return_constraint = TypeEnvConstraint::new(
2277 call_expr_id,
2278 TypeRepr::from_rust_string(return_type_str),
2279 format!("return type of macro {}()", func_name_str),
2280 );
2281 type_env.add_constraint(return_constraint);
2282 }
2283 }
2284
2285 fn collect_compound_constraints(&mut self, compound: &CompoundStmt, type_env: &mut TypeEnv) {
2287 for item in &compound.items {
2288 match item {
2289 BlockItem::Decl(decl) => {
2290 self.collect_decl_initializer_constraints(decl, type_env);
2292 }
2293 BlockItem::Stmt(Stmt::Expr(Some(expr), _)) => {
2294 self.collect_expr_constraints(expr, type_env);
2295 }
2296 BlockItem::Stmt(Stmt::Return(Some(expr), _)) => {
2297 self.collect_expr_constraints(expr, type_env);
2298 }
2299 BlockItem::Stmt(Stmt::Compound(inner)) => {
2300 self.collect_compound_constraints(inner, type_env);
2301 }
2302 _ => {}
2303 }
2304 }
2305 }
2306
2307 fn collect_decl_initializer_constraints(&mut self, decl: &Declaration, type_env: &mut TypeEnv) {
2309 for init_decl in &decl.declarators {
2310 if let Some(ref init) = init_decl.init {
2311 self.collect_initializer_constraints(init, type_env);
2312 }
2313 }
2314 }
2315
2316 fn collect_initializer_constraints(&mut self, init: &Initializer, type_env: &mut TypeEnv) {
2318 match init {
2319 Initializer::Expr(expr) => {
2320 self.collect_expr_constraints(expr, type_env);
2321 }
2322 Initializer::List(items) => {
2323 for item in items {
2324 self.collect_initializer_constraints(&item.init, type_env);
2325 }
2326 }
2327 }
2328 }
2329
2330 fn parse_rust_type_string(&self, type_str: &str) -> Type {
2332 let normalized = type_str
2334 .replace("* mut", "*mut")
2335 .replace("* const", "*const");
2336 let trimmed = normalized.trim();
2337
2338 if let Some(rest) = trimmed.strip_prefix("*mut ") {
2340 return Type::Pointer(
2341 Box::new(self.parse_rust_type_string(rest)),
2342 TypeQualifiers::default(),
2343 );
2344 }
2345 if let Some(rest) = trimmed.strip_prefix("*const ") {
2346 return Type::Pointer(
2347 Box::new(self.parse_rust_type_string(rest)),
2348 TypeQualifiers { is_const: true, ..Default::default() },
2349 );
2350 }
2351
2352 match trimmed {
2354 "()" => Type::Void,
2355 "c_char" => Type::Char,
2356 "c_int" => Type::Int,
2357 "c_uint" => Type::UnsignedInt,
2358 "c_long" => Type::Long,
2359 "c_ulong" => Type::UnsignedLong,
2360 "bool" => Type::Bool,
2361 "usize" => Type::UnsignedLong,
2362 "isize" => Type::Long,
2363 _ => {
2364 if let Some(interned) = self.interner.lookup(trimmed) {
2366 Type::TypedefName(interned)
2367 } else {
2368 Type::Unknown
2369 }
2370 }
2371 }
2372 }
2373}
2374
2375#[cfg(test)]
2376mod tests {
2377 use super::*;
2378
2379 #[test]
2380 fn test_type_display() {
2381 let interner = StringInterner::new();
2382
2383 assert_eq!(Type::Int.display(&interner), "int");
2384 assert_eq!(Type::UnsignedLong.display(&interner), "unsigned long");
2385 assert_eq!(
2386 Type::Pointer(Box::new(Type::Char), TypeQualifiers::default()).display(&interner),
2387 "char*"
2388 );
2389 }
2390
2391 #[test]
2392 fn test_scope_management() {
2393 let mut interner = StringInterner::new();
2394 let x = interner.intern("x");
2395 let mut analyzer = SemanticAnalyzer::new(&interner, None, None);
2396
2397 analyzer.define_symbol(Symbol {
2399 name: x,
2400 ty: Type::Int,
2401 loc: SourceLocation::default(),
2402 kind: SymbolKind::Variable,
2403 });
2404
2405 assert!(analyzer.lookup_symbol(x).is_some());
2406
2407 analyzer.push_scope();
2409
2410 assert!(analyzer.lookup_symbol(x).is_some());
2412
2413 analyzer.define_symbol(Symbol {
2415 name: x,
2416 ty: Type::Float, loc: SourceLocation::default(),
2418 kind: SymbolKind::Variable,
2419 });
2420
2421 let sym = analyzer.lookup_symbol(x).unwrap();
2423 assert_eq!(sym.ty, Type::Float);
2424
2425 analyzer.pop_scope();
2427
2428 let sym = analyzer.lookup_symbol(x).unwrap();
2430 assert_eq!(sym.ty, Type::Int);
2431 }
2432}