1use std::collections::{HashMap, HashSet};
7
8use crate::apidoc::ApidocDict;
9use crate::apidoc_patches::ApidocPatchSet;
10use crate::ast::{AssertKind, BlockItem, Expr, ExprKind};
11use crate::c_fn_decl::CFnDeclDict;
12use crate::fields_dict::FieldsDict;
13use crate::inline_fn::InlineFnDict;
14use crate::intern::{InternedStr, StringInterner};
15use crate::macro_def::{MacroDef, MacroKind, MacroTable};
16use crate::parser::{
17 parse_expression_from_tokens_ref_with_stats,
18 parse_expression_from_tokens_ref_with_generic_params,
19 parse_statement_from_tokens_ref_with_stats,
20 parse_statement_from_tokens_ref_with_generic_params,
21 parse_block_items_from_tokens_ref_with_stats,
22 parse_block_items_from_tokens_ref_with_generic_params,
23 ParseStats,
24};
25use crate::rust_decl::RustDeclDict;
26use crate::semantic::SemanticAnalyzer;
27use crate::preprocessor::Preprocessor;
28use crate::source::FileRegistry;
29use crate::token::{Token, TokenKind};
30use crate::type_env::{TypeConstraint, TypeEnv};
31use crate::type_repr::TypeRepr;
32
33#[derive(Debug, Clone, Copy)]
41pub struct NoExpandSymbols {
42 pub assert: InternedStr,
44 pub assert_: InternedStr,
46}
47
48impl NoExpandSymbols {
49 pub fn new(interner: &mut StringInterner) -> Self {
51 Self {
52 assert: interner.intern("assert"),
53 assert_: interner.intern("assert_"),
54 }
55 }
56
57 pub fn iter(&self) -> impl Iterator<Item = InternedStr> {
59 [self.assert, self.assert_].into_iter()
60 }
61}
62
63#[derive(Debug, Clone, Copy)]
69pub struct ExplicitExpandSymbols {
70 pub sv_any: InternedStr,
72 pub sv_flags: InternedStr,
74 pub cv_flags: InternedStr,
76 pub hek_flags: InternedStr,
78 pub expect: InternedStr,
80 pub likely: InternedStr,
82 pub unlikely: InternedStr,
84 pub cbool: InternedStr,
86 pub assert_underscore_: InternedStr,
88 pub str_with_len: InternedStr,
90 pub int2ptr: InternedStr,
92 pub assert_not_rok: InternedStr,
94 pub assert_not_glob: InternedStr,
96 pub mutable_ptr: InternedStr,
98}
99
100impl ExplicitExpandSymbols {
101 pub fn new(interner: &mut StringInterner) -> Self {
103 Self {
104 sv_any: interner.intern("SvANY"),
105 sv_flags: interner.intern("SvFLAGS"),
106 cv_flags: interner.intern("CvFLAGS"),
107 hek_flags: interner.intern("HEK_FLAGS"),
108 expect: interner.intern("EXPECT"),
109 likely: interner.intern("LIKELY"),
110 unlikely: interner.intern("UNLIKELY"),
111 cbool: interner.intern("cBOOL"),
112 assert_underscore_: interner.intern("__ASSERT_"),
113 str_with_len: interner.intern("STR_WITH_LEN"),
114 int2ptr: interner.intern("INT2PTR"),
115 assert_not_rok: interner.intern("assert_not_ROK"),
116 assert_not_glob: interner.intern("assert_not_glob"),
117 mutable_ptr: interner.intern("MUTABLE_PTR"),
118 }
119 }
120
121 pub fn iter(&self) -> impl Iterator<Item = InternedStr> {
123 [
124 self.sv_any,
125 self.sv_flags,
126 self.cv_flags,
127 self.hek_flags,
128 self.expect,
129 self.likely,
130 self.unlikely,
131 self.cbool,
132 self.assert_underscore_,
133 self.str_with_len,
134 self.int2ptr,
135 self.assert_not_rok,
136 self.assert_not_glob,
137 self.mutable_ptr,
138 ].into_iter()
139 }
140}
141
142#[derive(Debug, Clone)]
144pub enum ParseResult {
145 Expression(Box<Expr>),
147 Statement(Vec<BlockItem>),
149 Unparseable(Option<String>),
151}
152
153#[derive(Debug, Clone)]
162pub struct MacroParam {
163 pub name: InternedStr,
165 pub expr: Expr,
167}
168
169impl MacroParam {
170 pub fn new(name: InternedStr, loc: crate::source::SourceLocation) -> Self {
172 Self {
173 name,
174 expr: Expr::new(ExprKind::Ident(name), loc),
175 }
176 }
177
178 pub fn expr_id(&self) -> crate::ast::ExprId {
180 self.expr.id
181 }
182}
183
184#[derive(Debug, Clone, Copy, PartialEq, Eq)]
186pub enum InferStatus {
187 Pending,
189 TypeComplete,
191 TypeIncomplete,
193 TypeUnknown,
195}
196
197impl Default for InferStatus {
198 fn default() -> Self {
199 Self::Pending
200 }
201}
202
203fn collect_literal_string_params(entry: &crate::apidoc::ApidocEntry, info: &mut MacroInferInfo) {
207 use crate::apidoc::ApidocEntry;
208
209 for (i, arg) in entry.args.iter().enumerate() {
210 if ApidocEntry::is_literal_string_keyword(&arg.ty) {
211 info.literal_string_params.insert(i);
212 }
213 }
214}
215
216fn collect_generic_params(entry: &crate::apidoc::ApidocEntry, info: &mut MacroInferInfo) {
220 use crate::apidoc::ApidocEntry;
221
222 const PARAM_NAMES: [char; 7] = ['T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
223 let mut param_idx = 0;
224
225 for (i, arg) in entry.args.iter().enumerate() {
227 if ApidocEntry::is_type_param_keyword(&arg.ty) {
228 if param_idx < PARAM_NAMES.len() {
229 let name = PARAM_NAMES[param_idx].to_string();
230 info.generic_type_params.insert(i as i32, name);
231 param_idx += 1;
232 }
233 }
234 }
235
236 if entry.returns_type_param() {
238 let name = if let Some(first_name) = info.generic_type_params.get(&0) {
241 first_name.clone()
242 } else if param_idx < PARAM_NAMES.len() {
243 PARAM_NAMES[param_idx].to_string()
244 } else {
245 "T".to_string()
246 };
247 info.generic_type_params.insert(-1, name); }
249}
250
251#[derive(Debug, Clone)]
253pub struct MacroInferInfo {
254 pub name: InternedStr,
256 pub is_target: bool,
258 pub has_body: bool,
260 pub is_function: bool,
262
263 pub uses: HashSet<InternedStr>,
265 pub used_by: HashSet<InternedStr>,
267
268 pub is_thx_dependent: bool,
270
271 pub has_token_pasting: bool,
273
274 pub params: Vec<MacroParam>,
276
277 pub parse_result: ParseResult,
279
280 pub type_env: TypeEnv,
282
283 pub args_infer_status: InferStatus,
285
286 pub return_infer_status: InferStatus,
288
289 pub generic_type_params: HashMap<i32, String>,
296
297 pub literal_string_params: HashSet<usize>,
302
303 pub function_call_count: usize,
305 pub deref_count: usize,
307
308 pub called_functions: HashSet<InternedStr>,
310 pub calls_unavailable: bool,
312 pub apidoc_suppressed: bool,
317
318 pub resolved_param_types: Vec<String>,
322
323 pub resolved_return_type: Option<String>,
325
326 pub const_pointer_positions: HashSet<usize>,
328
329 pub is_bool_return: bool,
331}
332
333impl MacroInferInfo {
334 pub fn new(name: InternedStr) -> Self {
336 Self {
337 name,
338 is_target: false,
339 has_body: false,
340 is_function: false,
341 uses: HashSet::new(),
342 used_by: HashSet::new(),
343 is_thx_dependent: false,
344 has_token_pasting: false,
345 params: Vec::new(),
346 parse_result: ParseResult::Unparseable(None),
347 type_env: TypeEnv::new(),
348 args_infer_status: InferStatus::Pending,
349 return_infer_status: InferStatus::Pending,
350 generic_type_params: HashMap::new(),
351 literal_string_params: HashSet::new(),
352 function_call_count: 0,
353 deref_count: 0,
354 called_functions: HashSet::new(),
355 calls_unavailable: false,
356 apidoc_suppressed: false,
357 resolved_param_types: Vec::new(),
358 resolved_return_type: None,
359 const_pointer_positions: HashSet::new(),
360 is_bool_return: false,
361 }
362 }
363
364 pub fn has_unsafe_ops(&self) -> bool {
366 self.function_call_count > 0 || self.deref_count > 0
367 }
368
369 pub fn is_unavailable_for_codegen(&self) -> bool {
375 self.calls_unavailable || self.apidoc_suppressed
376 }
377
378 pub fn find_param_expr_id(&self, name: InternedStr) -> Option<crate::ast::ExprId> {
380 self.params.iter()
381 .find(|p| p.name == name)
382 .map(|p| p.expr_id())
383 }
384
385 pub fn is_fully_confirmed(&self) -> bool {
387 self.args_infer_status == InferStatus::TypeComplete
388 && self.return_infer_status == InferStatus::TypeComplete
389 }
390
391 pub fn add_use(&mut self, used_macro: InternedStr) {
393 self.uses.insert(used_macro);
394 }
395
396 pub fn add_used_by(&mut self, user_macro: InternedStr) {
398 self.used_by.insert(user_macro);
399 }
400
401 pub fn is_expression(&self) -> bool {
403 matches!(self.parse_result, ParseResult::Expression(_))
404 }
405
406 pub fn is_statement(&self) -> bool {
408 matches!(self.parse_result, ParseResult::Statement(_))
409 }
410
411 pub fn is_parseable(&self) -> bool {
413 !matches!(self.parse_result, ParseResult::Unparseable(_))
414 }
415
416 pub fn get_return_type(&self) -> Option<&crate::type_repr::TypeRepr> {
421 let mut best: Option<(&crate::type_repr::TypeRepr, u8)> = None;
424
425 for c in &self.type_env.return_constraints {
427 let tier = c.ty.confidence_tier();
428 if best.is_none() || tier < best.unwrap().1 {
429 best = Some((&c.ty, tier));
430 }
431 }
432
433 if let ParseResult::Expression(ref expr) = self.parse_result {
435 if let Some(constraints) = self.type_env.get_expr_constraints(expr.id) {
436 for c in constraints {
437 let tier = c.ty.confidence_tier();
438 if best.is_none() || tier < best.unwrap().1 {
439 best = Some((&c.ty, tier));
440 }
441 }
442 }
443 }
444
445 best.map(|(ty, _)| ty)
446 }
447}
448
449pub struct MacroInferContext {
453 pub macros: HashMap<InternedStr, MacroInferInfo>,
455
456 pub confirmed: HashSet<InternedStr>,
458
459 pub unconfirmed: HashSet<InternedStr>,
461
462 pub unknown: HashSet<InternedStr>,
464
465 pub debug_macros: HashSet<String>,
467
468 pub macro_param_types: HashMap<String, Vec<(String, String)>>,
472}
473
474impl MacroInferContext {
475 pub fn new() -> Self {
477 Self {
478 macros: HashMap::new(),
479 confirmed: HashSet::new(),
480 unconfirmed: HashSet::new(),
481 unknown: HashSet::new(),
482 debug_macros: HashSet::new(),
483 macro_param_types: HashMap::new(),
484 }
485 }
486
487 pub fn set_debug_macros(&mut self, macros: impl IntoIterator<Item = String>) {
489 self.debug_macros = macros.into_iter().collect();
490 }
491
492 pub fn is_debug_target(&self, name: &str) -> bool {
494 self.debug_macros.contains(name)
495 }
496
497 pub fn register(&mut self, info: MacroInferInfo) {
499 let name = info.name;
500 self.macros.insert(name, info);
501 }
502
503 pub fn get(&self, name: InternedStr) -> Option<&MacroInferInfo> {
505 self.macros.get(&name)
506 }
507
508 pub fn get_mut(&mut self, name: InternedStr) -> Option<&mut MacroInferInfo> {
510 self.macros.get_mut(&name)
511 }
512
513 pub fn apply_apidoc_suppressions(
520 &mut self,
521 patches: &ApidocPatchSet,
522 interner: &StringInterner,
523 ) -> usize {
524 let mut count = 0usize;
525 for name_str in patches.skip_codegen.keys() {
526 if let Some(interned) = interner.lookup(name_str) {
527 if let Some(info) = self.macros.get_mut(&interned) {
528 info.apidoc_suppressed = true;
529 count += 1;
530 }
531 }
532 }
533 count
534 }
535
536 pub fn build_use_relations(&mut self) {
540 let use_pairs: Vec<(InternedStr, InternedStr)> = self
542 .macros
543 .iter()
544 .flat_map(|(user, info)| {
545 info.uses
546 .iter()
547 .map(move |used| (*user, *used))
548 })
549 .collect();
550
551 for (user, used) in use_pairs {
553 if let Some(used_info) = self.macros.get_mut(&used) {
554 used_info.add_used_by(user);
555 }
556 }
557 }
558
559 pub fn classify_initial(&mut self) {
563 for (name, info) in &self.macros {
564 if info.is_fully_confirmed() {
565 self.confirmed.insert(*name);
566 } else if info.args_infer_status == InferStatus::TypeUnknown
567 || info.return_infer_status == InferStatus::TypeUnknown
568 {
569 self.unknown.insert(*name);
570 } else {
571 self.unconfirmed.insert(*name);
572 }
573 }
574 }
575
576 pub fn get_inference_candidates(&self) -> Vec<InternedStr> {
581 let mut candidates: Vec<_> = self
582 .unconfirmed
583 .iter()
584 .filter(|name| {
585 if let Some(info) = self.macros.get(name) {
586 info.uses.iter().all(|used| {
588 self.confirmed.contains(used) || !self.macros.contains_key(used)
589 })
590 } else {
591 false
592 }
593 })
594 .copied()
595 .collect();
596
597 candidates.sort_by_key(|name| {
599 self.macros
600 .get(name)
601 .map(|info| info.uses.len())
602 .unwrap_or(0)
603 });
604
605 candidates
606 }
607
608 pub fn mark_confirmed(&mut self, name: InternedStr) {
610 self.unconfirmed.remove(&name);
611 self.confirmed.insert(name);
612 if let Some(info) = self.macros.get_mut(&name) {
613 info.args_infer_status = InferStatus::TypeComplete;
614 info.return_infer_status = InferStatus::TypeComplete;
615 }
616 }
617
618 pub fn cache_param_types(&mut self, name: InternedStr, interner: &StringInterner) {
623 let mut temp_cache = HashMap::new();
624 self.cache_param_types_to(name, interner, &mut temp_cache);
625 self.macro_param_types.extend(temp_cache);
626 }
627
628 pub fn cache_param_types_to(
630 &self,
631 name: InternedStr,
632 interner: &StringInterner,
633 cache: &mut HashMap<String, Vec<(String, String)>>,
634 ) {
635 let info = match self.macros.get(&name) {
636 Some(info) => info,
637 None => return,
638 };
639
640 let macro_name = interner.get(name).to_string();
641 let mut param_types = Vec::new();
642
643 for param in &info.params {
644 let param_name = interner.get(param.name).to_string();
645
646 let type_str = if let Some(expr_ids) = info.type_env.param_to_exprs.get(¶m.name) {
648 let mut found_type = None;
650 for expr_id in expr_ids {
651 if let Some(constraints) = info.type_env.expr_constraints.get(expr_id) {
652 for c in constraints {
653 if !c.ty.is_void() {
654 found_type = Some(c.ty.to_rust_string(interner));
655 break;
656 }
657 }
658 }
659 if found_type.is_some() {
660 break;
661 }
662 }
663 found_type
664 } else {
665 let expr_id = param.expr_id();
667 info.type_env.expr_constraints.get(&expr_id)
668 .and_then(|constraints| constraints.first())
669 .map(|c| c.ty.to_rust_string(interner))
670 };
671
672 if let Some(ty) = type_str {
673 param_types.push((param_name, ty));
674 }
675 }
676
677 if !param_types.is_empty() {
678 cache.insert(macro_name, param_types);
679 }
680 }
681
682 pub fn get_macro_param_types(&self) -> &HashMap<String, Vec<(String, String)>> {
684 &self.macro_param_types
685 }
686
687 pub fn mark_args_unknown(&mut self, name: InternedStr) {
689 if let Some(info) = self.macros.get_mut(&name) {
690 info.args_infer_status = InferStatus::TypeUnknown;
691 }
692 }
693
694 pub fn mark_return_unknown(&mut self, name: InternedStr) {
696 if let Some(info) = self.macros.get_mut(&name) {
697 info.return_infer_status = InferStatus::TypeUnknown;
698 }
699 }
700
701 pub fn move_to_unknown(&mut self, name: InternedStr) {
703 self.unconfirmed.remove(&name);
704 self.unknown.insert(name);
705 }
706
707 pub fn stats(&self) -> MacroInferStats {
709 let mut args_unknown = 0;
710 let mut return_unknown = 0;
711 for info in self.macros.values() {
712 if info.args_infer_status == InferStatus::TypeUnknown {
713 args_unknown += 1;
714 }
715 if info.return_infer_status == InferStatus::TypeUnknown {
716 return_unknown += 1;
717 }
718 }
719 MacroInferStats {
720 total: self.macros.len(),
721 confirmed: self.confirmed.len(),
722 unconfirmed: self.unconfirmed.len(),
723 args_unknown,
724 return_unknown,
725 }
726 }
727
728 pub fn build_macro_info(
734 &self,
735 def: &MacroDef,
736 pp: &mut Preprocessor,
737 typedefs: &HashSet<InternedStr>,
738 thx_symbols: (InternedStr, InternedStr, InternedStr),
739 no_expand: NoExpandSymbols,
740 perl_build_mode: crate::perl_config::PerlBuildMode,
741 ) -> (MacroInferInfo, bool, bool) {
742 let mut info = MacroInferInfo::new(def.name);
743 info.is_target = def.is_target;
744 info.has_body = !def.body.is_empty();
745 info.is_function = matches!(def.kind, MacroKind::Function { .. });
746
747 let params: Vec<InternedStr> = if let MacroKind::Function { params, .. } = &def.kind {
749 for ¶m_name in params {
750 info.params.push(MacroParam::new(param_name, crate::source::SourceLocation::default()));
751 }
752 params.clone()
753 } else {
754 Vec::new()
755 };
756
757 let has_pasting_direct = def.body.iter().any(|t| matches!(t.kind, TokenKind::HashHash));
759
760 for sym in no_expand.iter() {
763 pp.add_skip_expand_macro(sym);
764 }
765
766 let mut in_progress = HashSet::new();
767 in_progress.insert(def.name); let (expanded_tokens, called_macros) = match pp.expand_macro_body_for_inference(
770 &def.body,
771 ¶ms,
772 &[], &mut in_progress,
774 ) {
775 Ok(result) => result,
776 Err(_) => {
777 (def.body.clone(), HashSet::new())
779 }
780 };
781
782 let has_cannot = expanded_tokens.iter().any(|t| {
784 matches!(&t.kind, TokenKind::StringLit(s) if s == b"CANNOT")
785 });
786 if has_cannot {
787 info.calls_unavailable = true;
788 return (info, has_pasting_direct, false);
789 }
790
791 let expanded_tokens = inject_comma_after_assert_underscore(
793 &expanded_tokens,
794 &no_expand,
795 );
796
797 self.collect_uses_from_called(&called_macros, &mut info);
799
800 let (sym_athx, sym_tthx, sym_my_perl) = thx_symbols;
805 let has_thx = if perl_build_mode.is_threaded() {
806 let has_thx_from_uses = info.uses.contains(&sym_athx) || info.uses.contains(&sym_tthx);
807 let has_my_perl = expanded_tokens.iter().any(|t| {
808 matches!(t.kind, TokenKind::Ident(id) if id == sym_my_perl)
809 });
810 has_thx_from_uses || has_my_perl
811 } else {
812 false
813 };
814
815 info.has_token_pasting = has_pasting_direct;
817 info.is_thx_dependent = has_thx;
818
819 let interner = pp.interner();
821 let files = pp.files();
822
823 let generic_params: HashMap<InternedStr, usize> = params.iter()
825 .enumerate()
826 .map(|(i, &name)| (name, i))
827 .collect();
828
829 let (parse_result, stats, detected_type_params) = self.try_parse_tokens(
830 &expanded_tokens, interner, files, typedefs, generic_params,
831 );
832 info.parse_result = parse_result;
833 info.function_call_count = stats.function_call_count;
834 info.deref_count = stats.deref_count;
835
836 if !detected_type_params.is_empty() {
838 let param_names = ['T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
839 let mut idx = 0;
840 for (i, param) in params.iter().enumerate() {
841 if detected_type_params.contains(param) && idx < param_names.len() {
842 info.generic_type_params.insert(i as i32, param_names[idx].to_string());
843 idx += 1;
844 }
845 }
846 }
847
848 match &mut info.parse_result {
850 ParseResult::Expression(expr) => {
851 convert_assert_calls(expr, interner);
852 }
853 ParseResult::Statement(items) => {
854 for item in items {
855 if let BlockItem::Stmt(stmt) = item {
856 convert_assert_calls_in_stmt(stmt, interner);
857 }
858 }
859 }
860 ParseResult::Unparseable(_) => {}
861 }
862
863 match &info.parse_result {
865 ParseResult::Expression(expr) => {
866 Self::collect_function_calls_from_expr(expr, &mut info.called_functions);
867 }
868 ParseResult::Statement(block_items) => {
869 Self::collect_function_calls_from_block_items(block_items, &mut info.called_functions);
870 }
871 ParseResult::Unparseable(_) => {}
872 }
873
874 (info, has_pasting_direct, has_thx)
875 }
876
877 pub fn infer_macro_types<'a>(
883 &mut self,
884 name: InternedStr,
885 params: &[InternedStr],
886 interner: &'a StringInterner,
887 files: &'a FileRegistry,
888 apidoc: Option<&'a ApidocDict>,
889 fields_dict: Option<&'a FieldsDict>,
890 rust_decl_dict: Option<&'a RustDeclDict>,
891 inline_fn_dict: Option<&'a InlineFnDict>,
892 typedefs: &'a HashSet<InternedStr>,
893 return_types_cache: &HashMap<String, String>,
894 param_types_cache: &HashMap<String, Vec<(String, String)>>,
895 ) {
896 let macro_name_str = interner.get(name);
897 let is_debug = self.is_debug_target(macro_name_str);
898
899 if is_debug {
900 eprintln!("\n[DEBUG infer_macro_types] macro={}", macro_name_str);
901 eprintln!(" params: {:?}", params.iter().map(|p| interner.get(*p)).collect::<Vec<_>>());
902 }
903
904 let info = match self.macros.get_mut(&name) {
905 Some(info) => info,
906 None => return,
907 };
908
909 if let ParseResult::Expression(ref expr) = info.parse_result {
911 let mut analyzer = SemanticAnalyzer::with_rust_decl_dict(
912 interner,
913 apidoc,
914 fields_dict,
915 rust_decl_dict,
916 inline_fn_dict,
917 );
918
919 analyzer.set_macro_return_types(return_types_cache);
921
922 analyzer.set_macro_param_types(param_types_cache);
924
925 analyzer.register_macro_params_from_apidoc(name, params, files, typedefs);
927
928 analyzer.collect_expr_constraints(expr, &mut info.type_env);
930
931 if is_debug {
933 eprintln!(" [type_env after collect_expr_constraints]");
934 for (expr_id, constraints) in &info.type_env.expr_constraints {
935 for c in constraints {
936 eprintln!(" expr_id={:?}: {} ({})", expr_id, c.ty.to_display_string(interner), c.context);
937 }
938 }
939 eprintln!(" [param_constraints]");
940 for (param_id, constraints) in &info.type_env.param_constraints {
941 for c in constraints {
942 eprintln!(" param={}: {} ({})", interner.get(*param_id), c.ty.to_display_string(interner), c.context);
943 }
944 }
945 eprintln!(" [param_to_exprs]");
946 for (param, expr_ids) in &info.type_env.param_to_exprs {
947 eprintln!(" param={}: {:?}", interner.get(*param), expr_ids);
948 }
949 }
950
951 if let Some(apidoc_dict) = apidoc {
953 let macro_name_str = interner.get(name);
954 if let Some(entry) = apidoc_dict.get(macro_name_str) {
955 if let Some(ref return_type) = entry.return_type {
956 let type_repr = TypeRepr::from_c_type_string(return_type, interner, files, typedefs);
957 info.type_env.add_return_constraint(TypeConstraint::new(
958 expr.id,
959 type_repr,
960 format!("return type of macro {}", macro_name_str),
961 ));
962 }
963
964 collect_generic_params(entry, info);
966
967 collect_literal_string_params(entry, info);
969 }
970 }
971 }
972
973 if let ParseResult::Statement(ref block_items) = info.parse_result {
975 let mut analyzer = SemanticAnalyzer::with_rust_decl_dict(
976 interner,
977 apidoc,
978 fields_dict,
979 rust_decl_dict,
980 inline_fn_dict,
981 );
982
983 analyzer.set_macro_return_types(return_types_cache);
985
986 analyzer.set_macro_param_types(param_types_cache);
988
989 analyzer.register_macro_params_from_apidoc(name, params, files, typedefs);
991
992 for item in block_items {
994 if let BlockItem::Stmt(stmt) = item {
995 analyzer.collect_stmt_constraints(stmt, &mut info.type_env);
996 }
997 }
998 }
999 }
1000
1001 pub fn get_macro_return_type(&self, name: InternedStr, interner: &StringInterner) -> Option<(String, String)> {
1003 self.macros.get(&name).and_then(|info| {
1004 info.get_return_type().map(|ty| {
1005 (interner.get(name).to_string(), ty.to_rust_string(interner))
1006 })
1007 })
1008 }
1009
1010 fn collect_uses_from_called(
1015 &self,
1016 called_macros: &HashSet<InternedStr>,
1017 info: &mut MacroInferInfo,
1018 ) {
1019 for &id in called_macros {
1020 if id != info.name {
1021 info.add_use(id);
1022 }
1023 }
1024 }
1025
1026 fn has_toplevel_semicolon(tokens: &[Token]) -> bool {
1028 let mut depth = 0;
1029 for t in tokens {
1030 match t.kind {
1031 TokenKind::LParen | TokenKind::LBrace | TokenKind::LBracket => depth += 1,
1032 TokenKind::RParen | TokenKind::RBrace | TokenKind::RBracket => {
1033 if depth > 0 { depth -= 1; }
1034 }
1035 TokenKind::Semi if depth == 0 => return true,
1036 _ => {}
1037 }
1038 }
1039 false
1040 }
1041
1042 fn try_parse_tokens(
1047 &self,
1048 tokens: &[crate::token::Token],
1049 interner: &StringInterner,
1050 files: &FileRegistry,
1051 typedefs: &HashSet<InternedStr>,
1052 generic_params: HashMap<InternedStr, usize>,
1053 ) -> (ParseResult, ParseStats, HashSet<InternedStr>) {
1054 if tokens.is_empty() {
1055 return (ParseResult::Unparseable(Some("empty token sequence".to_string())), ParseStats::default(), HashSet::new());
1056 }
1057
1058 let first_significant = tokens.iter().find(|t| {
1060 !matches!(t.kind, TokenKind::Space | TokenKind::Newline)
1061 });
1062
1063 let is_statement_start = first_significant
1065 .is_some_and(|t| matches!(t.kind, TokenKind::KwDo | TokenKind::KwIf));
1066 if is_statement_start {
1067 if generic_params.is_empty() {
1068 match parse_statement_from_tokens_ref_with_stats(tokens.to_vec(), interner, files, typedefs) {
1069 Ok((stmt, stats)) => {
1070 return (
1071 ParseResult::Statement(vec![BlockItem::Stmt(stmt)]),
1072 stats,
1073 HashSet::new(),
1074 );
1075 }
1076 Err(_) => {} }
1078 } else {
1079 match parse_statement_from_tokens_ref_with_generic_params(tokens.to_vec(), interner, files, typedefs, generic_params.clone()) {
1080 Ok((stmt, stats, detected)) => {
1081 return (
1082 ParseResult::Statement(vec![BlockItem::Stmt(stmt)]),
1083 stats,
1084 detected,
1085 );
1086 }
1087 Err(_) => {} }
1089 }
1090 }
1091
1092 if Self::has_toplevel_semicolon(tokens) {
1094 if generic_params.is_empty() {
1095 match parse_block_items_from_tokens_ref_with_stats(tokens.to_vec(), interner, files, typedefs) {
1096 Ok((items, stats)) => {
1097 return (
1098 ParseResult::Statement(items),
1099 stats,
1100 HashSet::new(),
1101 );
1102 }
1103 Err(_) => {} }
1105 } else {
1106 match parse_block_items_from_tokens_ref_with_generic_params(tokens.to_vec(), interner, files, typedefs, generic_params.clone()) {
1107 Ok((items, stats, detected)) => {
1108 return (
1109 ParseResult::Statement(items),
1110 stats,
1111 detected,
1112 );
1113 }
1114 Err(_) => {} }
1116 }
1117 }
1118
1119 if generic_params.is_empty() {
1121 match parse_expression_from_tokens_ref_with_stats(tokens.to_vec(), interner, files, typedefs) {
1122 Ok((expr, stats)) => (
1123 ParseResult::Expression(Box::new(expr)),
1124 stats,
1125 HashSet::new(),
1126 ),
1127 Err(err) => (ParseResult::Unparseable(Some(err.format_with_files(files))), ParseStats::default(), HashSet::new()),
1128 }
1129 } else {
1130 match parse_expression_from_tokens_ref_with_generic_params(tokens.to_vec(), interner, files, typedefs, generic_params) {
1131 Ok((expr, stats, detected)) => (
1132 ParseResult::Expression(Box::new(expr)),
1133 stats,
1134 detected,
1135 ),
1136 Err(err) => (ParseResult::Unparseable(Some(err.format_with_files(files))), ParseStats::default(), HashSet::new()),
1137 }
1138 }
1139 }
1140
1141 pub fn analyze_all_macros<'a>(
1146 &mut self,
1147 pp: &mut Preprocessor,
1148 apidoc: Option<&'a ApidocDict>,
1149 apidoc_patches: Option<&'a ApidocPatchSet>,
1150 fields_dict: Option<&'a FieldsDict>,
1151 rust_decl_dict: Option<&'a RustDeclDict>,
1152 mut inline_fn_dict: Option<&'a mut InlineFnDict>,
1153 c_fn_decl_dict: Option<&'a CFnDeclDict>,
1154 typedefs: &HashSet<InternedStr>,
1155 thx_symbols: (InternedStr, InternedStr, InternedStr),
1156 no_expand: NoExpandSymbols,
1157 perl_build_mode: crate::perl_config::PerlBuildMode,
1158 ) {
1159 let mut thx_initial = HashSet::new();
1161 let mut pasting_initial = HashSet::new();
1162
1163 let target_macros: Vec<MacroDef> = pp.macros().iter_target_macros().cloned().collect();
1165
1166 for def in &target_macros {
1167 let (info, has_pasting, has_thx) = self.build_macro_info(
1168 def, pp, typedefs, thx_symbols, no_expand, perl_build_mode
1169 );
1170 if has_pasting {
1171 pasting_initial.insert(def.name);
1172 }
1173 if has_thx {
1174 thx_initial.insert(def.name);
1175 }
1176 self.register(info);
1177 }
1178
1179 if let Some(c_fn_dict) = c_fn_decl_dict {
1181 for (name, info) in &self.macros {
1182 let has_thx_from_fn_calls = info.called_functions.iter().any(|fn_name| {
1184 c_fn_dict.is_thx_dependent(*fn_name)
1185 });
1186 if has_thx_from_fn_calls && !thx_initial.contains(name) {
1187 thx_initial.insert(*name);
1188 }
1189 }
1190 }
1191
1192 self.build_use_relations();
1194
1195 self.propagate_flag_via_used_by(&thx_initial, true);
1197
1198 self.propagate_flag_via_used_by(&pasting_initial, false);
1200
1201 if let Some(patches) = apidoc_patches {
1205 let interner = pp.interner();
1206 let macro_hits = self.apply_apidoc_suppressions(patches, interner);
1207 let inline_hits = inline_fn_dict
1208 .as_mut()
1209 .map(|ifd| ifd.apply_apidoc_suppressions(patches, interner))
1210 .unwrap_or(0);
1211 let total = patches.skip_codegen.len();
1212 let unmatched = total.saturating_sub(macro_hits + inline_hits);
1213 eprintln!(
1214 "[apidoc-suppress] skip_codegen reflected: {} macro(s) + {} inline fn(s); \
1215 {} of {} entries unmatched (no such macro/inline; possibly stale skip-list)",
1216 macro_hits, inline_hits, unmatched, total,
1217 );
1218 }
1219
1220 {
1222 let interner = pp.interner();
1223 self.check_function_availability(
1224 rust_decl_dict,
1225 inline_fn_dict.as_deref(),
1226 interner,
1227 );
1228 }
1229
1230 if let Some(ref mut ifd) = inline_fn_dict {
1232 let interner = pp.interner();
1233 self.check_inline_fn_availability(ifd, rust_decl_dict, interner);
1234 }
1235
1236 if let Some(ref mut ifd) = inline_fn_dict {
1238 self.propagate_unavailable_cross_domain(ifd);
1239 } else {
1240 self.propagate_unavailable_via_used_by();
1241 }
1242
1243 for name in self.macros.keys().copied().collect::<Vec<_>>() {
1245 self.unconfirmed.insert(name);
1246 }
1247
1248 {
1250 let macro_table = pp.macros();
1251 let interner = pp.interner();
1252 let files = pp.files();
1253 self.infer_types_in_dependency_order(
1254 macro_table, interner, files, apidoc, fields_dict, rust_decl_dict,
1255 inline_fn_dict.as_deref(), typedefs
1256 );
1257 }
1258 }
1259
1260 fn propagate_flag_via_used_by(&mut self, initial_set: &HashSet<InternedStr>, is_thx: bool) {
1264 for name in initial_set {
1266 if let Some(info) = self.macros.get_mut(name) {
1267 if is_thx {
1268 info.is_thx_dependent = true;
1269 } else {
1270 info.has_token_pasting = true;
1271 }
1272 }
1273 }
1274
1275 let mut to_propagate: Vec<InternedStr> = initial_set.iter().copied().collect();
1277
1278 while let Some(name) = to_propagate.pop() {
1279 let used_by_list: Vec<InternedStr> = self.macros
1280 .get(&name)
1281 .map(|info| info.used_by.iter().copied().collect())
1282 .unwrap_or_default();
1283
1284 for user in used_by_list {
1285 if let Some(user_info) = self.macros.get_mut(&user) {
1286 let flag = if is_thx {
1287 &mut user_info.is_thx_dependent
1288 } else {
1289 &mut user_info.has_token_pasting
1290 };
1291 if !*flag {
1292 *flag = true;
1293 to_propagate.push(user);
1294 }
1295 }
1296 }
1297 }
1298 }
1299
1300 fn check_function_availability(
1305 &mut self,
1306 rust_decl_dict: Option<&RustDeclDict>,
1307 inline_fn_dict: Option<&InlineFnDict>,
1308 interner: &StringInterner,
1309 ) {
1310 let bindings_fns: std::collections::HashSet<&str> = rust_decl_dict
1312 .map(|d| d.fns.keys().map(|s| s.as_str()).collect())
1313 .unwrap_or_default();
1314
1315 let builtin_fns: std::collections::HashSet<&str> = [
1317 "__builtin_expect",
1318 "__builtin_offsetof",
1319 "offsetof",
1320 "__builtin_types_compatible_p",
1321 "__builtin_constant_p",
1322 "__builtin_choose_expr",
1323 "__builtin_unreachable",
1324 "__builtin_trap",
1325 "__builtin_assume",
1326 "__builtin_bswap16",
1327 "__builtin_bswap32",
1328 "__builtin_bswap64",
1329 "__builtin_popcount",
1330 "__builtin_clz",
1331 "__builtin_ctz",
1332 "pthread_mutex_lock",
1333 "pthread_mutex_unlock",
1334 "pthread_rwlock_rdlock",
1335 "pthread_rwlock_wrlock",
1336 "pthread_rwlock_unlock",
1337 "memchr",
1338 "memcpy",
1339 "memmove",
1340 "memset",
1341 "strlen",
1342 "strcmp",
1343 "strncmp",
1344 "strcpy",
1345 "strncpy",
1346 "ASSERT_IS_LITERAL",
1347 "ASSERT_IS_PTR",
1348 "ASSERT_NOT_PTR",
1349 ].into_iter().collect();
1350
1351 let macro_names: HashSet<InternedStr> = self.macros.keys().copied().collect();
1353
1354 let macro_names_list: Vec<InternedStr> = self.macros.keys().copied().collect();
1356 for name in macro_names_list {
1357 let called_functions: Vec<InternedStr> = self.macros
1358 .get(&name)
1359 .map(|info| info.called_functions.iter().copied().collect())
1360 .unwrap_or_default();
1361
1362 let mut has_unavailable = false;
1363 for called_fn in called_functions {
1364 let fn_name = interner.get(called_fn);
1365
1366 if macro_names.contains(&called_fn) {
1368 continue;
1369 }
1370
1371 if bindings_fns.contains(fn_name) {
1373 continue;
1374 }
1375
1376 if let Some(inline_fns) = inline_fn_dict {
1378 if inline_fns.get(called_fn).is_some() {
1379 continue;
1380 }
1381 }
1382
1383 if builtin_fns.contains(fn_name) {
1385 continue;
1386 }
1387
1388 has_unavailable = true;
1390 break;
1391 }
1392
1393 if has_unavailable {
1394 if let Some(info) = self.macros.get_mut(&name) {
1395 info.calls_unavailable = true;
1396 }
1397 }
1398 }
1399 }
1400
1401 fn propagate_unavailable_via_used_by(&mut self) {
1408 let initial_set: HashSet<InternedStr> = self.macros
1410 .iter()
1411 .filter(|(_, info)| info.is_unavailable_for_codegen())
1412 .map(|(name, _)| *name)
1413 .collect();
1414
1415 let mut to_propagate: Vec<InternedStr> = initial_set.into_iter().collect();
1417
1418 while let Some(name) = to_propagate.pop() {
1419 let used_by_list: Vec<InternedStr> = self.macros
1420 .get(&name)
1421 .map(|info| info.used_by.iter().copied().collect())
1422 .unwrap_or_default();
1423
1424 for user in used_by_list {
1425 if let Some(user_info) = self.macros.get_mut(&user) {
1426 if !user_info.calls_unavailable {
1427 user_info.calls_unavailable = true;
1428 to_propagate.push(user);
1429 }
1430 }
1431 }
1432 }
1433 }
1434
1435 fn check_inline_fn_availability(
1440 &self,
1441 inline_fn_dict: &mut InlineFnDict,
1442 rust_decl_dict: Option<&RustDeclDict>,
1443 interner: &StringInterner,
1444 ) {
1445 let bindings_fns: HashSet<&str> = rust_decl_dict
1447 .map(|d| d.fns.keys().map(|s| s.as_str()).collect())
1448 .unwrap_or_default();
1449
1450 let builtin_fns: HashSet<&str> = [
1452 "__builtin_expect",
1453 "__builtin_offsetof",
1454 "offsetof",
1455 "__builtin_types_compatible_p",
1456 "__builtin_constant_p",
1457 "__builtin_choose_expr",
1458 "__builtin_unreachable",
1459 "__builtin_trap",
1460 "__builtin_assume",
1461 "__builtin_bswap16",
1462 "__builtin_bswap32",
1463 "__builtin_bswap64",
1464 "__builtin_popcount",
1465 "__builtin_clz",
1466 "__builtin_ctz",
1467 "pthread_mutex_lock",
1468 "pthread_mutex_unlock",
1469 "pthread_rwlock_rdlock",
1470 "pthread_rwlock_wrlock",
1471 "pthread_rwlock_unlock",
1472 "memchr",
1473 "memcpy",
1474 "memmove",
1475 "memset",
1476 "strlen",
1477 "strcmp",
1478 "strncmp",
1479 "strcpy",
1480 "strncpy",
1481 "ASSERT_IS_LITERAL",
1482 "ASSERT_IS_PTR",
1483 "ASSERT_NOT_PTR",
1484 ].into_iter().collect();
1485
1486 let macro_names: HashSet<InternedStr> = self.macros.keys().copied().collect();
1488
1489 let entries: Vec<(InternedStr, Vec<InternedStr>)> = inline_fn_dict
1491 .called_functions_iter()
1492 .map(|(name, calls)| (*name, calls.iter().copied().collect()))
1493 .collect();
1494
1495 for (name, called_fns) in entries {
1496 let mut has_unavailable = false;
1497 for called_fn in called_fns {
1498 let fn_name = interner.get(called_fn);
1499
1500 if macro_names.contains(&called_fn) { continue; }
1501 if bindings_fns.contains(fn_name) { continue; }
1502 if inline_fn_dict.get(called_fn).is_some() { continue; }
1503 if builtin_fns.contains(fn_name) { continue; }
1504
1505 has_unavailable = true;
1506 break;
1507 }
1508
1509 if has_unavailable {
1510 inline_fn_dict.set_calls_unavailable(name);
1511 }
1512 }
1513 }
1514
1515 fn propagate_unavailable_cross_domain(
1525 &mut self,
1526 inline_fn_dict: &mut InlineFnDict,
1527 ) {
1528 loop {
1529 let mut changed = false;
1530
1531 let macro_names: Vec<InternedStr> = self.macros.keys().copied().collect();
1533 for name in ¯o_names {
1534 if !self.macros.get(name)
1535 .map(|i| i.is_unavailable_for_codegen())
1536 .unwrap_or(false)
1537 {
1538 continue;
1539 }
1540 let used_by_list: Vec<InternedStr> = self.macros
1541 .get(name)
1542 .map(|info| info.used_by.iter().copied().collect())
1543 .unwrap_or_default();
1544 for user in used_by_list {
1545 if let Some(user_info) = self.macros.get_mut(&user) {
1546 if !user_info.calls_unavailable {
1547 user_info.calls_unavailable = true;
1548 changed = true;
1549 }
1550 }
1551 }
1552 }
1553
1554 let inline_entries: Vec<(InternedStr, Vec<InternedStr>)> = inline_fn_dict
1557 .called_functions_iter()
1558 .map(|(name, calls)| (*name, calls.iter().copied().collect()))
1559 .collect();
1560 for (name, calls) in &inline_entries {
1561 if inline_fn_dict.is_calls_unavailable(*name) {
1562 continue;
1563 }
1564 let has_unavailable_inline = calls.iter().any(|called| {
1565 inline_fn_dict.get(*called).is_some()
1566 && inline_fn_dict.is_unavailable_for_codegen(*called)
1567 });
1568 if has_unavailable_inline {
1569 inline_fn_dict.set_calls_unavailable(*name);
1570 changed = true;
1571 }
1572 }
1573
1574 for name in ¯o_names {
1577 if self.macros.get(name)
1578 .map(|i| i.calls_unavailable)
1579 .unwrap_or(false)
1580 {
1581 continue;
1582 }
1583 let called_fns: Vec<InternedStr> = self.macros
1584 .get(name)
1585 .map(|info| info.called_functions.iter().copied().collect())
1586 .unwrap_or_default();
1587 let has_unavailable_inline = called_fns.iter().any(|called| {
1588 inline_fn_dict.get(*called).is_some()
1589 && inline_fn_dict.is_unavailable_for_codegen(*called)
1590 });
1591 if has_unavailable_inline {
1592 if let Some(info) = self.macros.get_mut(name) {
1593 info.calls_unavailable = true;
1594 changed = true;
1595 }
1596 }
1597 }
1598
1599 for (name, calls) in &inline_entries {
1602 if inline_fn_dict.is_calls_unavailable(*name) {
1603 continue;
1604 }
1605 let has_unavailable_macro = calls.iter().any(|called| {
1606 self.macros.get(called)
1607 .map(|info| info.is_unavailable_for_codegen())
1608 .unwrap_or(false)
1609 });
1610 if has_unavailable_macro {
1611 inline_fn_dict.set_calls_unavailable(*name);
1612 changed = true;
1613 }
1614 }
1615
1616 if !changed {
1617 break;
1618 }
1619 }
1620 }
1621
1622 fn infer_types_in_dependency_order<'a>(
1624 &mut self,
1625 macro_table: &MacroTable,
1626 interner: &'a StringInterner,
1627 files: &FileRegistry,
1628 apidoc: Option<&'a ApidocDict>,
1629 fields_dict: Option<&'a FieldsDict>,
1630 rust_decl_dict: Option<&'a RustDeclDict>,
1631 inline_fn_dict: Option<&'a InlineFnDict>,
1632 typedefs: &HashSet<InternedStr>,
1633 ) {
1634 let mut return_types_cache: HashMap<String, String> = HashMap::new();
1636 let mut param_types_cache: HashMap<String, Vec<(String, String)>> = HashMap::new();
1638
1639 loop {
1640 let candidates = self.get_inference_candidates();
1641 if candidates.is_empty() {
1642 let remaining: Vec<_> = self.unconfirmed.iter().copied().collect();
1644 for name in remaining {
1645 let params: Vec<InternedStr> = macro_table
1647 .get(name)
1648 .map(|def| match &def.kind {
1649 MacroKind::Function { params, .. } => params.clone(),
1650 MacroKind::Object => vec![],
1651 })
1652 .unwrap_or_default();
1653
1654 self.infer_macro_types(
1656 name, ¶ms, interner, files, apidoc, fields_dict, rust_decl_dict, inline_fn_dict, typedefs,
1657 &return_types_cache, ¶m_types_cache,
1658 );
1659
1660 let is_confirmed = self.macros.get(&name)
1662 .map(|info| info.get_return_type().is_some())
1663 .unwrap_or(false);
1664
1665 if is_confirmed {
1666 if let Some((macro_name, return_type)) = self.get_macro_return_type(name, interner) {
1667 return_types_cache.insert(macro_name, return_type);
1668 }
1669 self.mark_confirmed(name);
1670 self.cache_param_types_to(name, interner, &mut param_types_cache);
1671 } else {
1672 self.move_to_unknown(name);
1673 }
1674 }
1675 break;
1676 }
1677
1678 for name in candidates {
1679 let params: Vec<InternedStr> = macro_table
1681 .get(name)
1682 .map(|def| match &def.kind {
1683 MacroKind::Function { params, .. } => params.clone(),
1684 MacroKind::Object => vec![],
1685 })
1686 .unwrap_or_default();
1687
1688 self.infer_macro_types(
1690 name, ¶ms, interner, files, apidoc, fields_dict, rust_decl_dict, inline_fn_dict, typedefs,
1691 &return_types_cache, ¶m_types_cache,
1692 );
1693
1694 let is_confirmed = self.macros.get(&name)
1696 .map(|info| {
1697 info.get_return_type().is_some()
1700 })
1701 .unwrap_or(false);
1702
1703 if is_confirmed {
1704 if let Some((macro_name, return_type)) = self.get_macro_return_type(name, interner) {
1706 return_types_cache.insert(macro_name, return_type);
1707 }
1708 self.mark_confirmed(name);
1709 self.cache_param_types_to(name, interner, &mut param_types_cache);
1710 } else {
1711 self.move_to_unknown(name);
1712 }
1713 }
1714 }
1715
1716 self.macro_param_types = param_types_cache;
1718 }
1719
1720 pub fn collect_uses_from_expr(
1722 expr: &Expr,
1723 uses: &mut HashSet<InternedStr>,
1724 ) {
1725 match &expr.kind {
1726 ExprKind::Call { func, args } => {
1727 if let ExprKind::Ident(name) = &func.kind {
1729 uses.insert(*name);
1730 }
1731 Self::collect_uses_from_expr(func, uses);
1732 for arg in args {
1733 Self::collect_uses_from_expr(arg, uses);
1734 }
1735 }
1736 ExprKind::Ident(name) => {
1737 uses.insert(*name);
1738 }
1739 ExprKind::Binary { lhs, rhs, .. } => {
1740 Self::collect_uses_from_expr(lhs, uses);
1741 Self::collect_uses_from_expr(rhs, uses);
1742 }
1743 ExprKind::Cast { expr: inner, .. }
1744 | ExprKind::PreInc(inner)
1745 | ExprKind::PreDec(inner)
1746 | ExprKind::PostInc(inner)
1747 | ExprKind::PostDec(inner)
1748 | ExprKind::AddrOf(inner)
1749 | ExprKind::Deref(inner)
1750 | ExprKind::UnaryPlus(inner)
1751 | ExprKind::UnaryMinus(inner)
1752 | ExprKind::BitNot(inner)
1753 | ExprKind::LogNot(inner)
1754 | ExprKind::Sizeof(inner) => {
1755 Self::collect_uses_from_expr(inner, uses);
1756 }
1757 ExprKind::Index { expr: base, index } => {
1758 Self::collect_uses_from_expr(base, uses);
1759 Self::collect_uses_from_expr(index, uses);
1760 }
1761 ExprKind::Member { expr: base, .. } | ExprKind::PtrMember { expr: base, .. } => {
1762 Self::collect_uses_from_expr(base, uses);
1763 }
1764 ExprKind::Conditional { cond, then_expr, else_expr } => {
1765 Self::collect_uses_from_expr(cond, uses);
1766 Self::collect_uses_from_expr(then_expr, uses);
1767 Self::collect_uses_from_expr(else_expr, uses);
1768 }
1769 ExprKind::Assign { lhs, rhs, .. } => {
1770 Self::collect_uses_from_expr(lhs, uses);
1771 Self::collect_uses_from_expr(rhs, uses);
1772 }
1773 ExprKind::Comma { lhs, rhs } => {
1774 Self::collect_uses_from_expr(lhs, uses);
1775 Self::collect_uses_from_expr(rhs, uses);
1776 }
1777 ExprKind::BuiltinCall { args, .. } => {
1778 for arg in args {
1779 if let crate::ast::BuiltinArg::Expr(e) = arg {
1780 Self::collect_uses_from_expr(e, uses);
1781 }
1782 }
1783 }
1784 ExprKind::Assert { condition, .. } => {
1785 Self::collect_uses_from_expr(condition, uses);
1786 }
1787 _ => {}
1788 }
1789 }
1790
1791 pub fn collect_function_calls_from_expr(
1793 expr: &Expr,
1794 calls: &mut HashSet<InternedStr>,
1795 ) {
1796 match &expr.kind {
1797 ExprKind::Call { func, args } => {
1798 if let ExprKind::Ident(name) = &func.kind {
1800 calls.insert(*name);
1801 }
1802 Self::collect_function_calls_from_expr(func, calls);
1803 for arg in args {
1804 Self::collect_function_calls_from_expr(arg, calls);
1805 }
1806 }
1807 ExprKind::Binary { lhs, rhs, .. } => {
1808 Self::collect_function_calls_from_expr(lhs, calls);
1809 Self::collect_function_calls_from_expr(rhs, calls);
1810 }
1811 ExprKind::Cast { expr: inner, .. }
1812 | ExprKind::PreInc(inner)
1813 | ExprKind::PreDec(inner)
1814 | ExprKind::PostInc(inner)
1815 | ExprKind::PostDec(inner)
1816 | ExprKind::AddrOf(inner)
1817 | ExprKind::Deref(inner)
1818 | ExprKind::UnaryPlus(inner)
1819 | ExprKind::UnaryMinus(inner)
1820 | ExprKind::BitNot(inner)
1821 | ExprKind::LogNot(inner)
1822 | ExprKind::Sizeof(inner) => {
1823 Self::collect_function_calls_from_expr(inner, calls);
1824 }
1825 ExprKind::Index { expr: base, index } => {
1826 Self::collect_function_calls_from_expr(base, calls);
1827 Self::collect_function_calls_from_expr(index, calls);
1828 }
1829 ExprKind::Member { expr: base, .. } | ExprKind::PtrMember { expr: base, .. } => {
1830 Self::collect_function_calls_from_expr(base, calls);
1831 }
1832 ExprKind::Conditional { cond, then_expr, else_expr } => {
1833 Self::collect_function_calls_from_expr(cond, calls);
1834 Self::collect_function_calls_from_expr(then_expr, calls);
1835 Self::collect_function_calls_from_expr(else_expr, calls);
1836 }
1837 ExprKind::Assign { lhs, rhs, .. } => {
1838 Self::collect_function_calls_from_expr(lhs, calls);
1839 Self::collect_function_calls_from_expr(rhs, calls);
1840 }
1841 ExprKind::Comma { lhs, rhs } => {
1842 Self::collect_function_calls_from_expr(lhs, calls);
1843 Self::collect_function_calls_from_expr(rhs, calls);
1844 }
1845 ExprKind::StmtExpr(compound) => {
1846 Self::collect_function_calls_from_block_items(&compound.items, calls);
1847 }
1848 ExprKind::BuiltinCall { args, .. } => {
1849 for arg in args {
1850 if let crate::ast::BuiltinArg::Expr(e) = arg {
1851 Self::collect_function_calls_from_expr(e, calls);
1852 }
1853 }
1854 }
1855 ExprKind::Assert { condition, .. } => {
1856 Self::collect_function_calls_from_expr(condition, calls);
1857 }
1858 _ => {}
1859 }
1860 }
1861
1862 pub fn collect_function_calls_from_block_items(
1864 items: &[BlockItem],
1865 calls: &mut HashSet<InternedStr>,
1866 ) {
1867 for item in items {
1868 match item {
1869 BlockItem::Stmt(stmt) => {
1870 Self::collect_function_calls_from_stmt(stmt, calls);
1871 }
1872 BlockItem::Decl(decl) => {
1873 Self::collect_function_calls_from_decl(decl, calls);
1874 }
1875 }
1876 }
1877 }
1878
1879 fn collect_function_calls_from_decl(
1881 decl: &crate::ast::Declaration,
1882 calls: &mut HashSet<InternedStr>,
1883 ) {
1884 for init_decl in &decl.declarators {
1885 if let Some(init) = &init_decl.init {
1886 Self::collect_function_calls_from_initializer(init, calls);
1887 }
1888 }
1889 }
1890
1891 fn collect_function_calls_from_initializer(
1893 init: &crate::ast::Initializer,
1894 calls: &mut HashSet<InternedStr>,
1895 ) {
1896 match init {
1897 crate::ast::Initializer::Expr(expr) => {
1898 Self::collect_function_calls_from_expr(expr, calls);
1899 }
1900 crate::ast::Initializer::List(items) => {
1901 for item in items {
1902 Self::collect_function_calls_from_initializer(&item.init, calls);
1903 }
1904 }
1905 }
1906 }
1907
1908 fn collect_function_calls_from_stmt(
1910 stmt: &crate::ast::Stmt,
1911 calls: &mut HashSet<InternedStr>,
1912 ) {
1913 use crate::ast::{Stmt, ForInit};
1914 match stmt {
1915 Stmt::Expr(Some(expr), _) => {
1916 Self::collect_function_calls_from_expr(expr, calls);
1917 }
1918 Stmt::If { cond, then_stmt, else_stmt, .. } => {
1919 Self::collect_function_calls_from_expr(cond, calls);
1920 Self::collect_function_calls_from_stmt(then_stmt, calls);
1921 if let Some(else_s) = else_stmt {
1922 Self::collect_function_calls_from_stmt(else_s, calls);
1923 }
1924 }
1925 Stmt::While { cond, body, .. } => {
1926 Self::collect_function_calls_from_expr(cond, calls);
1927 Self::collect_function_calls_from_stmt(body, calls);
1928 }
1929 Stmt::DoWhile { body, cond, .. } => {
1930 Self::collect_function_calls_from_stmt(body, calls);
1931 Self::collect_function_calls_from_expr(cond, calls);
1932 }
1933 Stmt::For { init, cond, step, body, .. } => {
1934 if let Some(for_init) = init {
1935 match for_init {
1936 ForInit::Expr(expr) => {
1937 Self::collect_function_calls_from_expr(expr, calls);
1938 }
1939 ForInit::Decl(_) => {
1940 }
1942 }
1943 }
1944 if let Some(cond_expr) = cond {
1945 Self::collect_function_calls_from_expr(cond_expr, calls);
1946 }
1947 if let Some(step_expr) = step {
1948 Self::collect_function_calls_from_expr(step_expr, calls);
1949 }
1950 Self::collect_function_calls_from_stmt(body, calls);
1951 }
1952 Stmt::Compound(compound) => {
1953 Self::collect_function_calls_from_block_items(&compound.items, calls);
1954 }
1955 Stmt::Return(Some(expr), _) => {
1956 Self::collect_function_calls_from_expr(expr, calls);
1957 }
1958 Stmt::Switch { expr, body, .. } => {
1959 Self::collect_function_calls_from_expr(expr, calls);
1960 Self::collect_function_calls_from_stmt(body, calls);
1961 }
1962 Stmt::Label { stmt, .. } | Stmt::Case { stmt, .. } | Stmt::Default { stmt, .. } => {
1963 Self::collect_function_calls_from_stmt(stmt, calls);
1964 }
1965 _ => {}
1966 }
1967 }
1968
1969 pub fn resolve_param_and_return_types(
1976 &mut self,
1977 interner: &mut StringInterner,
1978 rust_decl_dict: Option<&crate::rust_decl::RustDeclDict>,
1979 inline_fn_dict: &crate::inline_fn::InlineFnDict,
1980 ) {
1981 self.propagate_macro_return_types(interner);
1989
1990 let sorted = self.topological_sort_for_resolve();
1992
1993 let mut callee_const_params: HashMap<InternedStr, HashSet<usize>> = HashMap::new();
1995 Self::seed_callee_const(interner, rust_decl_dict, inline_fn_dict, &mut callee_const_params);
1996
1997 let mut bool_return_set: HashSet<InternedStr> = HashSet::new();
1999 Self::seed_bool_returns(interner, rust_decl_dict, inline_fn_dict, &mut bool_return_set);
2000
2001 for name in &sorted {
2003 let info = match self.macros.get(name) {
2004 Some(info) => info,
2005 None => continue,
2006 };
2007 if !info.is_parseable() || info.calls_unavailable || !info.is_function {
2008 continue;
2009 }
2010
2011 let must_mut = crate::rust_codegen::collect_must_mut_pointer_params(
2013 &info.parse_result,
2014 &info.params,
2015 &callee_const_params,
2016 );
2017 let mut const_positions = HashSet::new();
2018 for (i, param) in info.params.iter().enumerate() {
2019 if !must_mut.contains(¶m.name) {
2020 if Self::param_has_pointer_type_static(&info.type_env, param) {
2022 const_positions.insert(i);
2023 }
2024 }
2025 }
2026 if !const_positions.is_empty() {
2027 callee_const_params.insert(*name, const_positions.clone());
2028 }
2029
2030 let is_bool = if let ParseResult::Expression(expr) = &info.parse_result {
2032 crate::rust_codegen::is_boolean_expr_with_context(
2033 expr, &bool_return_set, &bool_return_set,
2034 )
2035 } else {
2036 false
2037 };
2038 if is_bool {
2039 bool_return_set.insert(*name);
2040 }
2041
2042 let info_mut = self.macros.get_mut(name).unwrap();
2044 info_mut.const_pointer_positions = const_positions;
2045 info_mut.is_bool_return = is_bool;
2046 }
2047 }
2048
2049 fn propagate_macro_return_types(&mut self, _interner: &StringInterner) {
2061 let sorted = self.topological_sort_for_resolve();
2062 let mut macro_returns: HashMap<InternedStr, TypeRepr> = HashMap::new();
2063
2064 for name in &sorted {
2065 let computed: Option<(crate::ast::ExprId, TypeRepr)> = {
2068 let info = match self.macros.get(name) {
2069 Some(i) => i,
2070 None => continue,
2071 };
2072 if !info.is_parseable() || info.calls_unavailable || !info.is_function {
2073 continue;
2074 }
2075 let ParseResult::Expression(ref expr) = info.parse_result else {
2076 continue;
2077 };
2078 compute_macro_return_type(expr, &info.type_env, ¯o_returns)
2079 .map(|ty| (expr.id, ty))
2080 };
2081
2082 if let Some((expr_id, ret_ty)) = computed {
2083 macro_returns.insert(*name, ret_ty.clone());
2084 let info_mut = self.macros.get_mut(name).unwrap();
2085 info_mut.type_env.add_return_constraint(TypeConstraint::new(
2086 expr_id,
2087 ret_ty,
2088 "macro return propagated from callee macros",
2089 ));
2090 }
2091
2092 let updates: Vec<(crate::ast::ExprId, TypeRepr)> = {
2103 let info = self.macros.get(name).unwrap();
2104 let mut acc = Vec::new();
2105 collect_macro_call_updates(&info.parse_result, ¯o_returns, &mut acc);
2106 acc
2107 };
2108 if !updates.is_empty() {
2109 let info_mut = self.macros.get_mut(name).unwrap();
2110 for (eid, ty) in updates {
2111 if !ty.is_concrete_pointer() {
2112 continue;
2113 }
2114 if let Some(cs) = info_mut.type_env.expr_constraints.get_mut(&eid) {
2115 let mut should_replace = false;
2116 cs.retain(|c| {
2117 let stale = c.context.starts_with("return type of macro ")
2118 && c.ty.is_void_pointer();
2119 if stale {
2120 should_replace = true;
2121 false
2122 } else {
2123 true
2124 }
2125 });
2126 if should_replace {
2127 cs.push(TypeConstraint::new(
2128 eid,
2129 ty,
2130 "return type from propagated callee macro (void* override)",
2131 ));
2132 }
2133 }
2134 }
2135 }
2136 }
2137 }
2138
2139 fn topological_sort_for_resolve(&self) -> Vec<InternedStr> {
2141 use std::collections::VecDeque;
2142 let target_macros: HashSet<InternedStr> = self.macros.iter()
2143 .filter(|(_, info)| info.is_target && info.has_body && info.is_function)
2144 .map(|(n, _)| *n)
2145 .collect();
2146
2147 let mut in_degree: HashMap<InternedStr, usize> = HashMap::new();
2148 for &name in &target_macros {
2149 in_degree.entry(name).or_insert(0);
2150 if let Some(info) = self.macros.get(&name) {
2151 for used in &info.uses {
2152 if target_macros.contains(used) {
2153 *in_degree.entry(name).or_insert(0) += 1;
2154 }
2155 }
2156 }
2157 }
2158
2159 let mut queue: VecDeque<InternedStr> = in_degree.iter()
2160 .filter(|(_, deg)| **deg == 0)
2161 .map(|(&name, _)| name)
2162 .collect();
2163 let mut result = Vec::new();
2164 while let Some(name) = queue.pop_front() {
2165 result.push(name);
2166 if let Some(info) = self.macros.get(&name) {
2167 for user in &info.used_by {
2168 if let Some(deg) = in_degree.get_mut(user) {
2169 *deg = deg.saturating_sub(1);
2170 if *deg == 0 {
2171 queue.push_back(*user);
2172 }
2173 }
2174 }
2175 }
2176 }
2177 for &name in &target_macros {
2179 if !result.contains(&name) {
2180 result.push(name);
2181 }
2182 }
2183 result
2184 }
2185
2186 fn param_has_pointer_type_static(type_env: &crate::type_env::TypeEnv, param: &MacroParam) -> bool {
2188 if let Some(expr_ids) = type_env.param_to_exprs.get(¶m.name) {
2189 for expr_id in expr_ids {
2190 if let Some(constraints) = type_env.expr_constraints.get(expr_id) {
2191 for c in constraints {
2192 if c.ty.has_outer_pointer() {
2193 return true;
2194 }
2195 }
2196 }
2197 }
2198 }
2199 let expr_id = param.expr_id();
2200 if let Some(constraints) = type_env.expr_constraints.get(&expr_id) {
2201 for c in constraints {
2202 if c.ty.has_outer_pointer() {
2203 return true;
2204 }
2205 }
2206 }
2207 false
2208 }
2209
2210 fn seed_callee_const(
2212 interner: &mut StringInterner,
2213 rust_decl_dict: Option<&crate::rust_decl::RustDeclDict>,
2214 inline_fn_dict: &crate::inline_fn::InlineFnDict,
2215 callee_const: &mut HashMap<InternedStr, HashSet<usize>>,
2216 ) {
2217 if let Some(dict) = rust_decl_dict {
2218 for (name, func) in &dict.fns {
2219 let name_id = interner.intern(name);
2220 let mut positions = HashSet::new();
2221 for (i, param) in func.params.iter().enumerate() {
2222 let normalized = param.ty.replace(" ", "");
2224 if normalized.contains("*const") {
2225 positions.insert(i);
2226 }
2227 }
2228 if !positions.is_empty() {
2229 callee_const.insert(name_id, positions);
2230 }
2231 }
2232 }
2233 for (name_id, fn_info) in inline_fn_dict.iter() {
2234 let mut positions = HashSet::new();
2235 for dd in &fn_info.declarator.derived {
2236 if let crate::ast::DerivedDecl::Function(param_list) = dd {
2237 for (i, param) in param_list.params.iter().enumerate() {
2238 if let Some(ref decl) = param.declarator {
2239 let has_const = decl.derived.iter().any(|d| {
2240 matches!(d, crate::ast::DerivedDecl::Pointer(q) if q.is_const)
2241 });
2242 if has_const {
2243 positions.insert(i);
2244 }
2245 }
2246 }
2247 break;
2248 }
2249 }
2250 if !positions.is_empty() {
2251 callee_const.insert(*name_id, positions);
2252 }
2253 }
2254 }
2255
2256 fn seed_bool_returns(
2258 interner: &mut StringInterner,
2259 rust_decl_dict: Option<&crate::rust_decl::RustDeclDict>,
2260 inline_fn_dict: &crate::inline_fn::InlineFnDict,
2261 bool_returns: &mut HashSet<InternedStr>,
2262 ) {
2263 if let Some(dict) = rust_decl_dict {
2264 for (name, func) in &dict.fns {
2265 if func.ret_ty.as_deref() == Some("bool") {
2266 let name_id = interner.intern(name);
2267 bool_returns.insert(name_id);
2268 }
2269 }
2270 }
2271 for (name_id, fn_info) in inline_fn_dict.iter() {
2272 let has_bool = fn_info.specs.type_specs.iter()
2273 .any(|ts| matches!(ts, crate::ast::TypeSpec::Bool));
2274 if has_bool {
2275 bool_returns.insert(*name_id);
2276 }
2277 }
2278 }
2279}
2280
2281impl Default for MacroInferContext {
2282 fn default() -> Self {
2283 Self::new()
2284 }
2285}
2286
2287fn collect_macro_call_updates(
2298 parse_result: &ParseResult,
2299 macro_returns: &HashMap<InternedStr, TypeRepr>,
2300 acc: &mut Vec<(crate::ast::ExprId, TypeRepr)>,
2301) {
2302 match parse_result {
2303 ParseResult::Expression(e) => visit_expr_for_calls(e, macro_returns, acc),
2304 ParseResult::Statement(items) => {
2305 for it in items {
2306 if let BlockItem::Stmt(stmt) = it {
2307 visit_stmt_for_calls(stmt, macro_returns, acc);
2308 }
2309 }
2310 }
2311 ParseResult::Unparseable(_) => {}
2312 }
2313}
2314
2315fn visit_expr_for_calls(
2316 expr: &Expr,
2317 macro_returns: &HashMap<InternedStr, TypeRepr>,
2318 acc: &mut Vec<(crate::ast::ExprId, TypeRepr)>,
2319) {
2320 if let ExprKind::Call { func, args } = &expr.kind {
2321 if let ExprKind::Ident(callee) = &func.kind {
2322 if let Some(ty) = macro_returns.get(callee) {
2323 acc.push((expr.id, ty.clone()));
2324 }
2325 }
2326 visit_expr_for_calls(func, macro_returns, acc);
2327 for a in args {
2328 visit_expr_for_calls(a, macro_returns, acc);
2329 }
2330 return;
2331 }
2332 walk_expr_children(expr, &mut |e| visit_expr_for_calls(e, macro_returns, acc));
2333}
2334
2335fn visit_stmt_for_calls(
2336 stmt: &crate::ast::Stmt,
2337 macro_returns: &HashMap<InternedStr, TypeRepr>,
2338 acc: &mut Vec<(crate::ast::ExprId, TypeRepr)>,
2339) {
2340 use crate::ast::Stmt;
2341 match stmt {
2342 Stmt::Compound(c) => {
2343 for it in &c.items {
2344 if let BlockItem::Stmt(s) = it {
2345 visit_stmt_for_calls(s, macro_returns, acc);
2346 }
2347 }
2348 }
2349 Stmt::Expr(Some(e), _) | Stmt::Return(Some(e), _) => {
2350 visit_expr_for_calls(e, macro_returns, acc)
2351 }
2352 Stmt::If { cond, then_stmt, else_stmt, .. } => {
2353 visit_expr_for_calls(cond, macro_returns, acc);
2354 visit_stmt_for_calls(then_stmt, macro_returns, acc);
2355 if let Some(es) = else_stmt {
2356 visit_stmt_for_calls(es, macro_returns, acc);
2357 }
2358 }
2359 Stmt::While { cond, body, .. } | Stmt::DoWhile { body, cond, .. } => {
2360 visit_expr_for_calls(cond, macro_returns, acc);
2361 visit_stmt_for_calls(body, macro_returns, acc);
2362 }
2363 Stmt::For { init, cond, step, body, .. } => {
2364 if let Some(crate::ast::ForInit::Expr(e)) = init {
2365 visit_expr_for_calls(e, macro_returns, acc);
2366 }
2367 if let Some(c) = cond {
2368 visit_expr_for_calls(c, macro_returns, acc);
2369 }
2370 if let Some(s) = step {
2371 visit_expr_for_calls(s, macro_returns, acc);
2372 }
2373 visit_stmt_for_calls(body, macro_returns, acc);
2374 }
2375 Stmt::Switch { expr, body, .. } => {
2376 visit_expr_for_calls(expr, macro_returns, acc);
2377 visit_stmt_for_calls(body, macro_returns, acc);
2378 }
2379 Stmt::Case { expr, stmt, .. } => {
2380 visit_expr_for_calls(expr, macro_returns, acc);
2381 visit_stmt_for_calls(stmt, macro_returns, acc);
2382 }
2383 Stmt::Default { stmt, .. } | Stmt::Label { stmt, .. } => {
2384 visit_stmt_for_calls(stmt, macro_returns, acc);
2385 }
2386 _ => {}
2387 }
2388}
2389
2390fn walk_expr_children<F: FnMut(&Expr)>(expr: &Expr, f: &mut F) {
2392 match &expr.kind {
2393 ExprKind::Ident(_)
2394 | ExprKind::IntLit(_)
2395 | ExprKind::UIntLit(_)
2396 | ExprKind::FloatLit(_)
2397 | ExprKind::CharLit(_)
2398 | ExprKind::StringLit(_)
2399 | ExprKind::SizeofType(_)
2400 | ExprKind::Alignof(_) => {}
2401 ExprKind::Call { func, args } => {
2402 f(func);
2403 for a in args { f(a); }
2404 }
2405 ExprKind::Index { expr: e, index } => { f(e); f(index); }
2406 ExprKind::Member { expr: e, .. } | ExprKind::PtrMember { expr: e, .. } => f(e),
2407 ExprKind::PostInc(e)
2408 | ExprKind::PostDec(e)
2409 | ExprKind::PreInc(e)
2410 | ExprKind::PreDec(e)
2411 | ExprKind::AddrOf(e)
2412 | ExprKind::Deref(e)
2413 | ExprKind::UnaryPlus(e)
2414 | ExprKind::UnaryMinus(e)
2415 | ExprKind::BitNot(e)
2416 | ExprKind::LogNot(e)
2417 | ExprKind::Sizeof(e) => f(e),
2418 ExprKind::Cast { expr: e, .. } => f(e),
2419 ExprKind::Binary { lhs, rhs, .. } => { f(lhs); f(rhs); }
2420 ExprKind::Assign { lhs, rhs, .. } => { f(lhs); f(rhs); }
2421 ExprKind::Conditional { cond, then_expr, else_expr } => {
2422 f(cond); f(then_expr); f(else_expr);
2423 }
2424 ExprKind::Comma { lhs, rhs } => { f(lhs); f(rhs); }
2425 ExprKind::CompoundLit { .. } => {}
2426 ExprKind::BuiltinCall { args, .. } => {
2427 for a in args {
2428 if let crate::ast::BuiltinArg::Expr(e) = a { f(e); }
2429 }
2430 }
2431 ExprKind::StmtExpr(_) => {}
2432 ExprKind::Assert { condition, .. } => f(condition),
2433 ExprKind::MacroCall { args, expanded, .. } => {
2434 for a in args { f(a); }
2435 f(expanded);
2436 }
2437 }
2438}
2439
2440fn compute_macro_return_type(
2441 expr: &Expr,
2442 env: &TypeEnv,
2443 macro_returns: &HashMap<InternedStr, TypeRepr>,
2444) -> Option<TypeRepr> {
2445 match &expr.kind {
2446 ExprKind::Conditional { then_expr, else_expr, .. } => {
2447 let then_ty = compute_macro_return_type(then_expr, env, macro_returns)
2448 .or_else(|| existing_constraint_type(then_expr.id, env));
2449 let else_ty = compute_macro_return_type(else_expr, env, macro_returns)
2450 .or_else(|| existing_constraint_type(else_expr.id, env));
2451 resolve_conditional_branches(then_ty, else_ty)
2452 }
2453 ExprKind::Call { func, .. } => {
2454 if let ExprKind::Ident(callee_name) = &func.kind {
2455 if let Some(ty) = macro_returns.get(callee_name) {
2456 return Some(ty.clone());
2457 }
2458 }
2459 existing_constraint_type(expr.id, env)
2460 }
2461 ExprKind::Comma { rhs, .. } => {
2463 compute_macro_return_type(rhs, env, macro_returns)
2464 .or_else(|| existing_constraint_type(rhs.id, env))
2465 }
2466 ExprKind::Binary { op, lhs, rhs } => {
2471 let lhs_ty = compute_macro_return_type(lhs, env, macro_returns)
2472 .or_else(|| existing_constraint_type(lhs.id, env));
2473 let rhs_ty = compute_macro_return_type(rhs, env, macro_returns)
2474 .or_else(|| existing_constraint_type(rhs.id, env));
2475 resolve_binary(op, lhs_ty, rhs_ty)
2476 }
2477 ExprKind::Cast { .. } => existing_constraint_type(expr.id, env),
2479 _ => existing_constraint_type(expr.id, env),
2481 }
2482}
2483
2484fn existing_constraint_type(
2486 expr_id: crate::ast::ExprId,
2487 env: &TypeEnv,
2488) -> Option<TypeRepr> {
2489 env.expr_constraints
2490 .get(&expr_id)
2491 .and_then(|cs| cs.first())
2492 .map(|c| c.ty.clone())
2493}
2494
2495fn resolve_binary(
2501 op: &crate::ast::BinOp,
2502 lhs_ty: Option<TypeRepr>,
2503 rhs_ty: Option<TypeRepr>,
2504) -> Option<TypeRepr> {
2505 use crate::ast::BinOp;
2506 match op {
2507 BinOp::Add | BinOp::Sub => {
2508 let lhs_is_ptr = lhs_ty.as_ref().is_some_and(|t| t.is_pointer_type());
2509 let rhs_is_ptr = rhs_ty.as_ref().is_some_and(|t| t.is_pointer_type());
2510 match (lhs_is_ptr, rhs_is_ptr) {
2511 (true, false) => lhs_ty,
2512 (false, true) => rhs_ty,
2513 _ => None, }
2515 }
2516 _ => None,
2517 }
2518}
2519
2520fn resolve_conditional_branches(
2524 then_ty: Option<TypeRepr>,
2525 else_ty: Option<TypeRepr>,
2526) -> Option<TypeRepr> {
2527 match (&then_ty, &else_ty) {
2528 (Some(t), Some(e)) => {
2529 if t.is_void_pointer() && e.is_concrete_pointer() {
2530 return else_ty;
2531 }
2532 if e.is_void_pointer() && t.is_concrete_pointer() {
2533 return then_ty;
2534 }
2535 then_ty
2539 }
2540 (Some(_), None) => then_ty,
2541 (None, Some(_)) => else_ty,
2542 (None, None) => None,
2543 }
2544}
2545
2546fn inject_comma_after_assert_underscore(
2553 tokens: &[Token],
2554 no_expand: &NoExpandSymbols,
2555) -> Vec<Token> {
2556 let assert_underscore = no_expand.assert_;
2557
2558 let mut result = Vec::with_capacity(tokens.len());
2559 let mut i = 0;
2560
2561 while i < tokens.len() {
2562 if matches!(tokens[i].kind, TokenKind::Ident(name) if name == assert_underscore) {
2563 result.push(tokens[i].clone());
2565 i += 1;
2566
2567 while i < tokens.len() && matches!(tokens[i].kind, TokenKind::Space | TokenKind::Newline) {
2569 result.push(tokens[i].clone());
2570 i += 1;
2571 }
2572
2573 if i < tokens.len() && matches!(tokens[i].kind, TokenKind::LParen) {
2575 let mut depth = 0;
2576 loop {
2577 if i >= tokens.len() {
2578 break;
2579 }
2580 match tokens[i].kind {
2581 TokenKind::LParen => depth += 1,
2582 TokenKind::RParen => {
2583 depth -= 1;
2584 if depth == 0 {
2585 result.push(tokens[i].clone());
2586 i += 1;
2587 break;
2588 }
2589 }
2590 _ => {}
2591 }
2592 result.push(tokens[i].clone());
2593 i += 1;
2594 }
2595
2596 let next_significant = tokens[i..].iter()
2598 .find(|t| !matches!(t.kind, TokenKind::Space | TokenKind::Newline));
2599 let needs_comma = next_significant
2600 .is_some_and(|t| !matches!(t.kind,
2601 TokenKind::Comma | TokenKind::RParen | TokenKind::Eof
2602 | TokenKind::Semi));
2603 if needs_comma {
2604 let loc = result.last().map(|t| t.loc.clone())
2605 .unwrap_or_default();
2606 result.push(Token::new(TokenKind::Comma, loc));
2607 }
2608 }
2609 } else {
2610 result.push(tokens[i].clone());
2611 i += 1;
2612 }
2613 }
2614
2615 result
2616}
2617
2618pub fn detect_assert_kind(name: &str) -> Option<AssertKind> {
2620 match name {
2621 "assert" => Some(AssertKind::Assert),
2622 "assert_" => Some(AssertKind::AssertUnderscore),
2623 _ => None,
2624 }
2625}
2626
2627pub fn convert_assert_calls(expr: &mut Expr, interner: &StringInterner) {
2632 match &mut expr.kind {
2633 ExprKind::Call { func, args } => {
2634 convert_assert_calls(func, interner);
2636 for arg in args.iter_mut() {
2637 convert_assert_calls(arg, interner);
2638 }
2639
2640 if let ExprKind::Ident(name) = &func.kind {
2642 let name_str = interner.get(*name);
2643 if let Some(kind) = detect_assert_kind(name_str) {
2644 if let Some(condition) = args.pop() {
2645 expr.kind = ExprKind::Assert {
2646 kind,
2647 condition: Box::new(condition),
2648 };
2649 }
2650 }
2651 }
2652 }
2653 ExprKind::Binary { lhs, rhs, .. } => {
2654 convert_assert_calls(lhs, interner);
2655 convert_assert_calls(rhs, interner);
2656 }
2657 ExprKind::Cast { expr: inner, .. }
2658 | ExprKind::PreInc(inner)
2659 | ExprKind::PreDec(inner)
2660 | ExprKind::PostInc(inner)
2661 | ExprKind::PostDec(inner)
2662 | ExprKind::AddrOf(inner)
2663 | ExprKind::Deref(inner)
2664 | ExprKind::UnaryPlus(inner)
2665 | ExprKind::UnaryMinus(inner)
2666 | ExprKind::BitNot(inner)
2667 | ExprKind::LogNot(inner)
2668 | ExprKind::Sizeof(inner) => {
2669 convert_assert_calls(inner, interner);
2670 }
2671 ExprKind::Index { expr: base, index } => {
2672 convert_assert_calls(base, interner);
2673 convert_assert_calls(index, interner);
2674 }
2675 ExprKind::Member { expr: base, .. } | ExprKind::PtrMember { expr: base, .. } => {
2676 convert_assert_calls(base, interner);
2677 }
2678 ExprKind::Conditional { cond, then_expr, else_expr } => {
2679 convert_assert_calls(cond, interner);
2680 convert_assert_calls(then_expr, interner);
2681 convert_assert_calls(else_expr, interner);
2682 }
2683 ExprKind::Assign { lhs, rhs, .. } => {
2684 convert_assert_calls(lhs, interner);
2685 convert_assert_calls(rhs, interner);
2686 }
2687 ExprKind::Comma { lhs, rhs } => {
2688 convert_assert_calls(lhs, interner);
2689 convert_assert_calls(rhs, interner);
2690 }
2691 ExprKind::Assert { condition, .. } => {
2692 convert_assert_calls(condition, interner);
2693 }
2694 ExprKind::CompoundLit { init, .. } => {
2695 for item in init {
2696 if let crate::ast::Initializer::Expr(e) = &mut item.init {
2697 convert_assert_calls(e, interner);
2698 }
2699 }
2700 }
2701 ExprKind::StmtExpr(compound) => {
2702 for item in &mut compound.items {
2703 if let BlockItem::Stmt(stmt) = item {
2704 convert_assert_calls_in_stmt(stmt, interner);
2705 }
2706 }
2707 }
2708 ExprKind::MacroCall { args, expanded, .. } => {
2710 for arg in args.iter_mut() {
2711 convert_assert_calls(arg, interner);
2712 }
2713 convert_assert_calls(expanded, interner);
2714 }
2715 ExprKind::BuiltinCall { args, .. } => {
2716 for arg in args.iter_mut() {
2717 if let crate::ast::BuiltinArg::Expr(e) = arg {
2718 convert_assert_calls(e, interner);
2719 }
2720 }
2721 }
2722 ExprKind::Ident(_)
2724 | ExprKind::IntLit(_)
2725 | ExprKind::UIntLit(_)
2726 | ExprKind::FloatLit(_)
2727 | ExprKind::CharLit(_)
2728 | ExprKind::StringLit(_)
2729 | ExprKind::SizeofType(_)
2730 | ExprKind::Alignof(_) => {}
2731 }
2732}
2733
2734pub fn convert_assert_calls_in_compound_stmt(compound: &mut crate::ast::CompoundStmt, interner: &StringInterner) {
2738 use crate::ast::BlockItem;
2739 for item in &mut compound.items {
2740 if let BlockItem::Stmt(s) = item {
2741 convert_assert_calls_in_stmt(s, interner);
2742 }
2743 }
2744}
2745
2746pub fn convert_assert_calls_in_stmt(stmt: &mut crate::ast::Stmt, interner: &StringInterner) {
2748 use crate::ast::Stmt;
2749 match stmt {
2750 Stmt::Expr(Some(expr), _) => convert_assert_calls(expr, interner),
2751 Stmt::If { cond, then_stmt, else_stmt, .. } => {
2752 convert_assert_calls(cond, interner);
2753 convert_assert_calls_in_stmt(then_stmt, interner);
2754 if let Some(else_s) = else_stmt {
2755 convert_assert_calls_in_stmt(else_s, interner);
2756 }
2757 }
2758 Stmt::While { cond, body, .. } => {
2759 convert_assert_calls(cond, interner);
2760 convert_assert_calls_in_stmt(body, interner);
2761 }
2762 Stmt::DoWhile { body, cond, .. } => {
2763 convert_assert_calls_in_stmt(body, interner);
2764 convert_assert_calls(cond, interner);
2765 }
2766 Stmt::For { init, cond, step, body, .. } => {
2767 if let Some(crate::ast::ForInit::Expr(e)) = init {
2768 convert_assert_calls(e, interner);
2769 }
2770 if let Some(c) = cond {
2771 convert_assert_calls(c, interner);
2772 }
2773 if let Some(s) = step {
2774 convert_assert_calls(s, interner);
2775 }
2776 convert_assert_calls_in_stmt(body, interner);
2777 }
2778 Stmt::Switch { expr, body, .. } => {
2779 convert_assert_calls(expr, interner);
2780 convert_assert_calls_in_stmt(body, interner);
2781 }
2782 Stmt::Return(Some(expr), _) => convert_assert_calls(expr, interner),
2783 Stmt::Compound(compound) => {
2784 for item in &mut compound.items {
2785 match item {
2786 BlockItem::Stmt(s) => convert_assert_calls_in_stmt(s, interner),
2787 BlockItem::Decl(_) => {}
2788 }
2789 }
2790 }
2791 Stmt::Label { stmt: s, .. }
2792 | Stmt::Case { stmt: s, .. }
2793 | Stmt::Default { stmt: s, .. } => {
2794 convert_assert_calls_in_stmt(s, interner);
2795 }
2796 _ => {}
2797 }
2798}
2799
2800#[derive(Debug, Clone, Copy)]
2802pub struct MacroInferStats {
2803 pub total: usize,
2804 pub confirmed: usize,
2805 pub unconfirmed: usize,
2806 pub args_unknown: usize,
2808 pub return_unknown: usize,
2810}
2811
2812impl std::fmt::Display for MacroInferStats {
2813 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2814 write!(
2815 f,
2816 "MacroInferStats {{ total: {}, confirmed: {}, unconfirmed: {}, args_unknown: {}, return_unknown: {} }}",
2817 self.total, self.confirmed, self.unconfirmed, self.args_unknown, self.return_unknown
2818 )
2819 }
2820}
2821
2822#[cfg(test)]
2823mod tests {
2824 use super::*;
2825 use crate::intern::StringInterner;
2826
2827 #[test]
2828 fn test_macro_infer_info_new() {
2829 let mut interner = StringInterner::new();
2830 let name = interner.intern("MY_MACRO");
2831
2832 let info = MacroInferInfo::new(name);
2833
2834 assert_eq!(info.name, name);
2835 assert!(!info.is_target);
2836 assert!(!info.is_thx_dependent);
2837 assert!(!info.has_token_pasting);
2838 assert!(info.uses.is_empty());
2839 assert!(info.used_by.is_empty());
2840 assert!(!info.is_parseable());
2841 assert_eq!(info.args_infer_status, InferStatus::Pending);
2842 assert_eq!(info.return_infer_status, InferStatus::Pending);
2843 }
2844
2845 #[test]
2846 fn test_macro_infer_context_register() {
2847 let mut interner = StringInterner::new();
2848 let name = interner.intern("FOO");
2849
2850 let mut ctx = MacroInferContext::new();
2851 let info = MacroInferInfo::new(name);
2852 ctx.register(info);
2853
2854 assert!(ctx.get(name).is_some());
2855 assert_eq!(ctx.macros.len(), 1);
2856 }
2857
2858 #[test]
2859 fn test_build_use_relations() {
2860 let mut interner = StringInterner::new();
2861 let foo = interner.intern("FOO");
2862 let bar = interner.intern("BAR");
2863 let baz = interner.intern("BAZ");
2864
2865 let mut ctx = MacroInferContext::new();
2866
2867 let mut foo_info = MacroInferInfo::new(foo);
2869 foo_info.add_use(bar);
2870 ctx.register(foo_info);
2871
2872 let mut bar_info = MacroInferInfo::new(bar);
2874 bar_info.add_use(baz);
2875 ctx.register(bar_info);
2876
2877 let baz_info = MacroInferInfo::new(baz);
2879 ctx.register(baz_info);
2880
2881 ctx.build_use_relations();
2883
2884 assert!(ctx.get(bar).unwrap().used_by.contains(&foo));
2886 assert!(ctx.get(baz).unwrap().used_by.contains(&bar));
2888 }
2889
2890 #[test]
2891 fn test_inference_candidates() {
2892 let mut interner = StringInterner::new();
2893 let foo = interner.intern("FOO");
2894 let bar = interner.intern("BAR");
2895 let baz = interner.intern("BAZ");
2896
2897 let mut ctx = MacroInferContext::new();
2898
2899 let mut foo_info = MacroInferInfo::new(foo);
2901 foo_info.add_use(bar);
2902 ctx.register(foo_info);
2903
2904 let mut bar_info = MacroInferInfo::new(bar);
2906 bar_info.add_use(baz);
2907 ctx.register(bar_info);
2908
2909 let mut baz_info = MacroInferInfo::new(baz);
2911 baz_info.args_infer_status = InferStatus::TypeComplete;
2912 baz_info.return_infer_status = InferStatus::TypeComplete;
2913 ctx.register(baz_info);
2914
2915 ctx.classify_initial();
2916
2917 assert!(ctx.confirmed.contains(&baz));
2919 assert!(ctx.unconfirmed.contains(&foo));
2920 assert!(ctx.unconfirmed.contains(&bar));
2921
2922 let candidates = ctx.get_inference_candidates();
2924 assert_eq!(candidates, vec![bar]);
2925
2926 ctx.mark_confirmed(bar);
2928 let candidates = ctx.get_inference_candidates();
2929 assert_eq!(candidates, vec![foo]);
2930 }
2931
2932 #[test]
2933 fn test_no_expand_symbols_new() {
2934 let mut interner = StringInterner::new();
2935 let symbols = NoExpandSymbols::new(&mut interner);
2936
2937 assert_eq!(interner.get(symbols.assert), "assert");
2938 assert_eq!(interner.get(symbols.assert_), "assert_");
2939 }
2940
2941 #[test]
2942 fn test_no_expand_symbols_iter() {
2943 let mut interner = StringInterner::new();
2944 let symbols = NoExpandSymbols::new(&mut interner);
2945
2946 let syms: Vec<_> = symbols.iter().collect();
2947 assert_eq!(syms.len(), 2);
2948 assert!(syms.contains(&symbols.assert));
2949 assert!(syms.contains(&symbols.assert_));
2950 }
2951
2952 #[test]
2953 fn test_explicit_expand_symbols_new() {
2954 let mut interner = StringInterner::new();
2955 let symbols = ExplicitExpandSymbols::new(&mut interner);
2956
2957 assert_eq!(interner.get(symbols.sv_any), "SvANY");
2958 assert_eq!(interner.get(symbols.sv_flags), "SvFLAGS");
2959 assert_eq!(interner.get(symbols.expect), "EXPECT");
2960 assert_eq!(interner.get(symbols.likely), "LIKELY");
2961 assert_eq!(interner.get(symbols.unlikely), "UNLIKELY");
2962 assert_eq!(interner.get(symbols.cbool), "cBOOL");
2963 assert_eq!(interner.get(symbols.assert_underscore_), "__ASSERT_");
2964 assert_eq!(interner.get(symbols.str_with_len), "STR_WITH_LEN");
2965 assert_eq!(interner.get(symbols.assert_not_rok), "assert_not_ROK");
2966 assert_eq!(interner.get(symbols.assert_not_glob), "assert_not_glob");
2967 assert_eq!(interner.get(symbols.mutable_ptr), "MUTABLE_PTR");
2968 }
2969
2970 #[test]
2971 fn test_explicit_expand_symbols_iter() {
2972 let mut interner = StringInterner::new();
2973 let symbols = ExplicitExpandSymbols::new(&mut interner);
2974
2975 let syms: Vec<_> = symbols.iter().collect();
2976 assert_eq!(syms.len(), 14);
2977 assert!(syms.contains(&symbols.sv_any));
2978 assert!(syms.contains(&symbols.sv_flags));
2979 assert!(syms.contains(&symbols.cv_flags));
2980 assert!(syms.contains(&symbols.hek_flags));
2981 assert!(syms.contains(&symbols.expect));
2982 assert!(syms.contains(&symbols.likely));
2983 assert!(syms.contains(&symbols.unlikely));
2984 assert!(syms.contains(&symbols.cbool));
2985 assert!(syms.contains(&symbols.assert_underscore_));
2986 assert!(syms.contains(&symbols.str_with_len));
2987 assert!(syms.contains(&symbols.assert_not_rok));
2988 assert!(syms.contains(&symbols.assert_not_glob));
2989 assert!(syms.contains(&symbols.mutable_ptr));
2990 }
2991}