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 assert_is_literal: InternedStr,
94 pub int2ptr: InternedStr,
96 pub assert_not_rok: InternedStr,
98 pub assert_not_glob: InternedStr,
100 pub mutable_ptr: InternedStr,
102}
103
104impl ExplicitExpandSymbols {
105 pub fn new(interner: &mut StringInterner) -> Self {
107 Self {
108 sv_any: interner.intern("SvANY"),
109 sv_flags: interner.intern("SvFLAGS"),
110 cv_flags: interner.intern("CvFLAGS"),
111 hek_flags: interner.intern("HEK_FLAGS"),
112 expect: interner.intern("EXPECT"),
113 likely: interner.intern("LIKELY"),
114 unlikely: interner.intern("UNLIKELY"),
115 cbool: interner.intern("cBOOL"),
116 assert_underscore_: interner.intern("__ASSERT_"),
117 str_with_len: interner.intern("STR_WITH_LEN"),
118 assert_is_literal: interner.intern("ASSERT_IS_LITERAL"),
119 int2ptr: interner.intern("INT2PTR"),
120 assert_not_rok: interner.intern("assert_not_ROK"),
121 assert_not_glob: interner.intern("assert_not_glob"),
122 mutable_ptr: interner.intern("MUTABLE_PTR"),
123 }
124 }
125
126 pub fn iter(&self) -> impl Iterator<Item = InternedStr> {
136 [
137 self.sv_any,
138 self.sv_flags,
139 self.cv_flags,
140 self.hek_flags,
141 self.expect,
142 self.likely,
143 self.unlikely,
144 self.cbool,
145 self.assert_underscore_,
146 self.str_with_len,
147 self.assert_is_literal,
148 self.int2ptr,
149 self.assert_not_rok,
150 self.assert_not_glob,
151 ].into_iter()
152 }
153}
154
155#[derive(Debug, Clone)]
157pub enum ParseResult {
158 Expression(Box<Expr>),
160 Statement(Vec<BlockItem>),
162 Unparseable(Option<String>),
164}
165
166#[derive(Debug, Clone)]
175pub struct MacroParam {
176 pub name: InternedStr,
178 pub expr: Expr,
180}
181
182impl MacroParam {
183 pub fn new(name: InternedStr, loc: crate::source::SourceLocation) -> Self {
185 Self {
186 name,
187 expr: Expr::new(ExprKind::Ident(name), loc),
188 }
189 }
190
191 pub fn expr_id(&self) -> crate::ast::ExprId {
193 self.expr.id
194 }
195}
196
197#[derive(Debug, Clone, Copy, PartialEq, Eq)]
199pub enum InferStatus {
200 Pending,
202 TypeComplete,
204 TypeIncomplete,
206 TypeUnknown,
208}
209
210impl Default for InferStatus {
211 fn default() -> Self {
212 Self::Pending
213 }
214}
215
216fn collect_literal_string_params(entry: &crate::apidoc::ApidocEntry, info: &mut MacroInferInfo) {
220 use crate::apidoc::ApidocEntry;
221
222 for (i, arg) in entry.args.iter().enumerate() {
223 if ApidocEntry::is_literal_string_keyword(&arg.ty) {
224 info.literal_string_params.insert(i);
225 }
226 }
227}
228
229fn collect_generic_params(entry: &crate::apidoc::ApidocEntry, info: &mut MacroInferInfo) {
233 use crate::apidoc::ApidocEntry;
234
235 const PARAM_NAMES: [char; 7] = ['T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
236 let mut param_idx = 0;
237
238 for (i, arg) in entry.args.iter().enumerate() {
240 if ApidocEntry::is_type_param_keyword(&arg.ty) {
241 if param_idx < PARAM_NAMES.len() {
242 let name = PARAM_NAMES[param_idx].to_string();
243 info.generic_type_params.insert(i as i32, name);
244 param_idx += 1;
245 }
246 }
247 }
248
249 if entry.returns_type_param() {
251 let name = if let Some(first_name) = info.generic_type_params.get(&0) {
254 first_name.clone()
255 } else if param_idx < PARAM_NAMES.len() {
256 PARAM_NAMES[param_idx].to_string()
257 } else {
258 "T".to_string()
259 };
260 info.generic_type_params.insert(-1, name); }
262}
263
264#[derive(Debug, Clone)]
266pub struct MacroInferInfo {
267 pub name: InternedStr,
269 pub is_target: bool,
271 pub has_body: bool,
273 pub is_function: bool,
275
276 pub uses: HashSet<InternedStr>,
278 pub used_by: HashSet<InternedStr>,
280
281 pub is_thx_dependent: bool,
283
284 pub has_token_pasting: bool,
286
287 pub params: Vec<MacroParam>,
289
290 pub parse_result: ParseResult,
292
293 pub type_env: TypeEnv,
295
296 pub args_infer_status: InferStatus,
298
299 pub return_infer_status: InferStatus,
301
302 pub generic_type_params: HashMap<i32, String>,
309
310 pub literal_string_params: HashSet<usize>,
315
316 pub function_call_count: usize,
318 pub deref_count: usize,
320
321 pub called_functions: HashSet<InternedStr>,
323 pub calls_unavailable: bool,
325 pub apidoc_suppressed: bool,
330 pub pair_return: bool,
334
335 pub resolved_param_types: Vec<String>,
339
340 pub resolved_return_type: Option<String>,
342
343 pub const_pointer_positions: HashSet<usize>,
345
346 pub is_bool_return: bool,
348
349 pub stmt_unrepresentable: bool,
354
355 pub local_usage: Option<crate::local_usage::LocalUsageAnalysis>,
358}
359
360impl MacroInferInfo {
361 pub fn new(name: InternedStr) -> Self {
363 Self {
364 name,
365 is_target: false,
366 has_body: false,
367 is_function: false,
368 uses: HashSet::new(),
369 used_by: HashSet::new(),
370 is_thx_dependent: false,
371 has_token_pasting: false,
372 params: Vec::new(),
373 parse_result: ParseResult::Unparseable(None),
374 type_env: TypeEnv::new(),
375 args_infer_status: InferStatus::Pending,
376 return_infer_status: InferStatus::Pending,
377 generic_type_params: HashMap::new(),
378 literal_string_params: HashSet::new(),
379 function_call_count: 0,
380 deref_count: 0,
381 called_functions: HashSet::new(),
382 calls_unavailable: false,
383 apidoc_suppressed: false,
384 pair_return: false,
385 resolved_param_types: Vec::new(),
386 resolved_return_type: None,
387 const_pointer_positions: HashSet::new(),
388 is_bool_return: false,
389 stmt_unrepresentable: false,
390 local_usage: None,
391 }
392 }
393
394 pub fn has_unsafe_ops(&self) -> bool {
396 self.function_call_count > 0 || self.deref_count > 0
397 }
398
399 pub fn is_unavailable_for_codegen(&self) -> bool {
412 self.calls_unavailable || self.apidoc_suppressed
413 }
414
415 pub fn find_param_expr_id(&self, name: InternedStr) -> Option<crate::ast::ExprId> {
417 self.params.iter()
418 .find(|p| p.name == name)
419 .map(|p| p.expr_id())
420 }
421
422 pub fn is_fully_confirmed(&self) -> bool {
424 self.args_infer_status == InferStatus::TypeComplete
425 && self.return_infer_status == InferStatus::TypeComplete
426 }
427
428 pub fn add_use(&mut self, used_macro: InternedStr) {
430 self.uses.insert(used_macro);
431 }
432
433 pub fn add_used_by(&mut self, user_macro: InternedStr) {
435 self.used_by.insert(user_macro);
436 }
437
438 pub fn is_expression(&self) -> bool {
440 matches!(self.parse_result, ParseResult::Expression(_))
441 }
442
443 pub fn is_statement(&self) -> bool {
445 matches!(self.parse_result, ParseResult::Statement(_))
446 }
447
448 pub fn is_parseable(&self) -> bool {
450 !matches!(self.parse_result, ParseResult::Unparseable(_))
451 }
452
453 pub fn get_return_type(&self) -> Option<&crate::type_repr::TypeRepr> {
458 let mut best: Option<(&crate::type_repr::TypeRepr, u8)> = None;
461
462 for c in &self.type_env.return_constraints {
464 let tier = c.ty.confidence_tier();
465 if best.is_none() || tier < best.unwrap().1 {
466 best = Some((&c.ty, tier));
467 }
468 }
469
470 if let ParseResult::Expression(ref expr) = self.parse_result {
472 if let Some(constraints) = self.type_env.get_expr_constraints(expr.id) {
473 for c in constraints {
474 let tier = c.ty.confidence_tier();
475 if best.is_none() || tier < best.unwrap().1 {
476 best = Some((&c.ty, tier));
477 }
478 }
479 }
480 }
481
482 best.map(|(ty, _)| ty)
483 }
484
485 pub fn is_statement_with_resolvable_params(&self) -> bool {
493 !self.stmt_unrepresentable
494 && matches!(self.parse_result, ParseResult::Statement(_))
495 && self.params.iter().all(|p| {
496 self.type_env.param_constraints.contains_key(&p.name)
497 || self
498 .type_env
499 .param_to_exprs
500 .get(&p.name)
501 .is_some_and(|ids| {
502 ids.iter()
503 .any(|id| self.type_env.expr_constraints.contains_key(id))
504 })
505 })
506 }
507}
508
509pub struct MacroInferContext {
513 pub macros: HashMap<InternedStr, MacroInferInfo>,
515
516 pub confirmed: HashSet<InternedStr>,
518
519 pub unconfirmed: HashSet<InternedStr>,
521
522 pub unknown: HashSet<InternedStr>,
524
525 pub debug_macros: HashSet<String>,
527
528 pub macro_param_types: HashMap<String, Vec<(String, String)>>,
532
533 pub return_override_names: HashSet<InternedStr>,
537}
538
539impl MacroInferContext {
540 pub fn new() -> Self {
542 Self {
543 macros: HashMap::new(),
544 confirmed: HashSet::new(),
545 unconfirmed: HashSet::new(),
546 unknown: HashSet::new(),
547 debug_macros: HashSet::new(),
548 macro_param_types: HashMap::new(),
549 return_override_names: HashSet::new(),
550 }
551 }
552
553 pub fn set_debug_macros(&mut self, macros: impl IntoIterator<Item = String>) {
555 self.debug_macros = macros.into_iter().collect();
556 }
557
558 pub fn is_debug_target(&self, name: &str) -> bool {
560 self.debug_macros.contains(name)
561 }
562
563 pub fn register(&mut self, info: MacroInferInfo) {
565 let name = info.name;
566 self.macros.insert(name, info);
567 }
568
569 pub fn get(&self, name: InternedStr) -> Option<&MacroInferInfo> {
571 self.macros.get(&name)
572 }
573
574 pub fn get_mut(&mut self, name: InternedStr) -> Option<&mut MacroInferInfo> {
576 self.macros.get_mut(&name)
577 }
578
579 pub fn apply_apidoc_suppressions(
586 &mut self,
587 patches: &ApidocPatchSet,
588 interner: &StringInterner,
589 ) -> usize {
590 let mut count = 0usize;
591 for name_str in patches.skip_codegen.keys() {
592 if let Some(interned) = interner.lookup(name_str) {
593 if let Some(info) = self.macros.get_mut(&interned) {
594 info.apidoc_suppressed = true;
595 count += 1;
596 }
597 }
598 }
599 count
600 }
601
602 pub fn build_use_relations(&mut self) {
606 let use_pairs: Vec<(InternedStr, InternedStr)> = self
608 .macros
609 .iter()
610 .flat_map(|(user, info)| {
611 info.uses
612 .iter()
613 .map(move |used| (*user, *used))
614 })
615 .collect();
616
617 for (user, used) in use_pairs {
619 if let Some(used_info) = self.macros.get_mut(&used) {
620 used_info.add_used_by(user);
621 }
622 }
623 }
624
625 pub fn classify_initial(&mut self) {
629 for (name, info) in &self.macros {
630 if info.is_fully_confirmed() {
631 self.confirmed.insert(*name);
632 } else if info.args_infer_status == InferStatus::TypeUnknown
633 || info.return_infer_status == InferStatus::TypeUnknown
634 {
635 self.unknown.insert(*name);
636 } else {
637 self.unconfirmed.insert(*name);
638 }
639 }
640 }
641
642 pub fn get_inference_candidates(&self) -> Vec<InternedStr> {
647 let mut candidates: Vec<_> = self
648 .unconfirmed
649 .iter()
650 .filter(|name| {
651 if let Some(info) = self.macros.get(name) {
652 info.uses.iter().all(|used| {
654 self.confirmed.contains(used) || !self.macros.contains_key(used)
655 })
656 } else {
657 false
658 }
659 })
660 .copied()
661 .collect();
662
663 candidates.sort_by_key(|name| {
667 (
668 self.macros
669 .get(name)
670 .map(|info| info.uses.len())
671 .unwrap_or(0),
672 *name,
673 )
674 });
675
676 candidates
677 }
678
679 pub fn mark_confirmed(&mut self, name: InternedStr) {
681 self.unconfirmed.remove(&name);
682 self.confirmed.insert(name);
683 if let Some(info) = self.macros.get_mut(&name) {
684 info.args_infer_status = InferStatus::TypeComplete;
685 info.return_infer_status = InferStatus::TypeComplete;
686 }
687 }
688
689 pub fn cache_param_types(&mut self, name: InternedStr, interner: &StringInterner) {
694 let mut temp_cache = HashMap::new();
695 self.cache_param_types_to(name, interner, &mut temp_cache);
696 self.macro_param_types.extend(temp_cache);
697 }
698
699 pub fn cache_param_types_to(
701 &self,
702 name: InternedStr,
703 interner: &StringInterner,
704 cache: &mut HashMap<String, Vec<(String, String)>>,
705 ) {
706 let info = match self.macros.get(&name) {
707 Some(info) => info,
708 None => return,
709 };
710
711 let macro_name = interner.get(name).to_string();
712 let mut param_types = Vec::new();
713
714 for param in &info.params {
715 let param_name = interner.get(param.name).to_string();
716
717 let type_str = if let Some(expr_ids) = info.type_env.param_to_exprs.get(¶m.name) {
719 let mut best: Option<(&crate::type_repr::TypeRepr, u8)> = None;
724 for expr_id in expr_ids {
725 if let Some(constraints) = info.type_env.expr_constraints.get(expr_id) {
726 for c in constraints {
727 if c.ty.is_void() {
728 continue;
729 }
730 let tier = c.ty.confidence_tier();
731 if best.is_none() || tier < best.unwrap().1 {
732 best = Some((&c.ty, tier));
733 }
734 }
735 }
736 }
737 best.map(|(ty, _)| ty.to_rust_string(interner))
738 } else {
739 let expr_id = param.expr_id();
741 info.type_env.expr_constraints.get(&expr_id)
742 .and_then(|constraints| constraints.first())
743 .map(|c| c.ty.to_rust_string(interner))
744 };
745
746 if let Some(ty) = type_str {
747 param_types.push((param_name, ty));
748 }
749 }
750
751 if !param_types.is_empty() {
752 cache.insert(macro_name, param_types);
753 }
754 }
755
756 pub fn get_macro_param_types(&self) -> &HashMap<String, Vec<(String, String)>> {
758 &self.macro_param_types
759 }
760
761 pub fn mark_args_unknown(&mut self, name: InternedStr) {
763 if let Some(info) = self.macros.get_mut(&name) {
764 info.args_infer_status = InferStatus::TypeUnknown;
765 }
766 }
767
768 pub fn mark_return_unknown(&mut self, name: InternedStr) {
770 if let Some(info) = self.macros.get_mut(&name) {
771 info.return_infer_status = InferStatus::TypeUnknown;
772 }
773 }
774
775 pub fn move_to_unknown(&mut self, name: InternedStr) {
777 self.unconfirmed.remove(&name);
778 self.unknown.insert(name);
779 }
780
781 pub fn stats(&self) -> MacroInferStats {
783 let mut args_unknown = 0;
784 let mut return_unknown = 0;
785 for info in self.macros.values() {
786 if info.args_infer_status == InferStatus::TypeUnknown {
787 args_unknown += 1;
788 }
789 if info.return_infer_status == InferStatus::TypeUnknown {
790 return_unknown += 1;
791 }
792 }
793 MacroInferStats {
794 total: self.macros.len(),
795 confirmed: self.confirmed.len(),
796 unconfirmed: self.unconfirmed.len(),
797 args_unknown,
798 return_unknown,
799 }
800 }
801
802 pub fn build_macro_info(
808 &self,
809 def: &MacroDef,
810 pp: &mut Preprocessor,
811 typedefs: &HashSet<InternedStr>,
812 thx_symbols: (InternedStr, InternedStr, InternedStr),
813 no_expand: NoExpandSymbols,
814 perl_build_mode: crate::perl_config::PerlBuildMode,
815 ) -> (MacroInferInfo, bool, bool) {
816 let mut info = MacroInferInfo::new(def.name);
817 info.is_target = def.is_target;
818 info.has_body = !def.body.is_empty();
819 info.is_function = matches!(def.kind, MacroKind::Function { .. });
820
821 let params: Vec<InternedStr> = if let MacroKind::Function { params, .. } = &def.kind {
823 for ¶m_name in params {
824 info.params.push(MacroParam::new(param_name, crate::source::SourceLocation::default()));
825 }
826 params.clone()
827 } else {
828 Vec::new()
829 };
830
831 let has_pasting_direct = def.body.iter().any(|t| matches!(t.kind, TokenKind::HashHash));
833
834 for sym in no_expand.iter() {
837 pp.add_skip_expand_macro(sym);
838 }
839
840 let mut in_progress = HashSet::new();
841 in_progress.insert(def.name); let (expanded_tokens, called_macros) = match pp.expand_macro_body_for_inference(
844 &def.body,
845 ¶ms,
846 &[], &mut in_progress,
848 ) {
849 Ok(result) => result,
850 Err(_) => {
851 (def.body.clone(), HashSet::new())
853 }
854 };
855
856 let has_cannot = expanded_tokens.iter().any(|t| {
858 matches!(&t.kind, TokenKind::StringLit(s) if s == b"CANNOT")
859 });
860 if has_cannot {
861 info.calls_unavailable = true;
862 return (info, has_pasting_direct, false);
863 }
864
865 let expanded_tokens = inject_comma_after_assert_underscore(
867 &expanded_tokens,
868 &no_expand,
869 );
870
871 self.collect_uses_from_called(&called_macros, &mut info);
873
874 let (sym_athx, sym_tthx, sym_my_perl) = thx_symbols;
879 let has_thx = if perl_build_mode.is_threaded() {
880 let has_thx_from_uses = info.uses.contains(&sym_athx) || info.uses.contains(&sym_tthx);
881 let has_my_perl = expanded_tokens.iter().any(|t| {
882 matches!(t.kind, TokenKind::Ident(id) if id == sym_my_perl)
883 });
884 has_thx_from_uses || has_my_perl
885 } else {
886 false
887 };
888
889 info.has_token_pasting = has_pasting_direct;
891 info.is_thx_dependent = has_thx;
892
893 let interner = pp.interner();
895 let files = pp.files();
896
897 let generic_params: HashMap<InternedStr, usize> = params.iter()
899 .enumerate()
900 .map(|(i, &name)| (name, i))
901 .collect();
902
903 let (parse_result, stats, detected_type_params) = self.try_parse_tokens(
904 &expanded_tokens, interner, files, typedefs, generic_params,
905 );
906 info.parse_result = parse_result;
907
908 match &info.parse_result {
914 ParseResult::Statement(items) => {
915 info.stmt_unrepresentable = items.iter().any(|it| match it {
916 BlockItem::Decl(_) => true,
917 BlockItem::Stmt(crate::ast::Stmt::Expr(Some(e), _)) => {
918 Self::expr_uses_typedef_as_value(e, typedefs)
919 }
920 BlockItem::Stmt(_) => false,
921 });
922 }
923 ParseResult::Expression(e) => {
924 info.stmt_unrepresentable = Self::expr_uses_typedef_as_value(e, typedefs);
925 }
926 ParseResult::Unparseable(_) => {}
927 }
928 info.function_call_count = stats.function_call_count;
929 info.deref_count = stats.deref_count;
930
931 if !detected_type_params.is_empty() {
933 let param_names = ['T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
934 let mut idx = 0;
935 for (i, param) in params.iter().enumerate() {
936 if detected_type_params.contains(param) && idx < param_names.len() {
937 info.generic_type_params.insert(i as i32, param_names[idx].to_string());
938 idx += 1;
939 }
940 }
941 }
942
943 match &mut info.parse_result {
945 ParseResult::Expression(expr) => {
946 convert_assert_calls(expr, interner);
947 }
948 ParseResult::Statement(items) => {
949 for item in items {
950 if let BlockItem::Stmt(stmt) = item {
951 convert_assert_calls_in_stmt(stmt, interner);
952 }
953 }
954 }
955 ParseResult::Unparseable(_) => {}
956 }
957
958 match &info.parse_result {
960 ParseResult::Expression(expr) => {
961 Self::collect_function_calls_from_expr(expr, &mut info.called_functions);
962 }
963 ParseResult::Statement(block_items) => {
964 Self::collect_function_calls_from_block_items(block_items, &mut info.called_functions);
965 }
966 ParseResult::Unparseable(_) => {}
967 }
968
969 (info, has_pasting_direct, has_thx)
970 }
971
972 pub fn infer_macro_types<'a>(
978 &mut self,
979 name: InternedStr,
980 params: &[InternedStr],
981 interner: &'a StringInterner,
982 files: &'a FileRegistry,
983 apidoc: Option<&'a ApidocDict>,
984 fields_dict: Option<&'a FieldsDict>,
985 rust_decl_dict: Option<&'a RustDeclDict>,
986 inline_fn_dict: Option<&'a InlineFnDict>,
987 typedefs: &'a HashSet<InternedStr>,
988 return_types_cache: &HashMap<String, String>,
989 param_types_cache: &HashMap<String, Vec<(String, String)>>,
990 ) {
991 let macro_name_str = interner.get(name);
992 let is_debug = self.is_debug_target(macro_name_str);
993 let is_return_override = self.return_override_names.contains(&name);
995
996 if is_debug {
997 eprintln!("\n[DEBUG infer_macro_types] macro={}", macro_name_str);
998 eprintln!(" params: {:?}", params.iter().map(|p| interner.get(*p)).collect::<Vec<_>>());
999 }
1000
1001 let info = match self.macros.get_mut(&name) {
1002 Some(info) => info,
1003 None => return,
1004 };
1005
1006 if let ParseResult::Expression(ref expr) = info.parse_result {
1008 let mut analyzer = SemanticAnalyzer::with_rust_decl_dict(
1009 interner,
1010 apidoc,
1011 fields_dict,
1012 rust_decl_dict,
1013 inline_fn_dict,
1014 );
1015
1016 analyzer.set_macro_return_types(return_types_cache);
1018
1019 analyzer.set_macro_param_types(param_types_cache);
1021
1022 analyzer.register_macro_params_from_apidoc(name, params, files, typedefs);
1024
1025 analyzer.collect_expr_constraints(expr, &mut info.type_env);
1027
1028 if is_debug {
1030 eprintln!(" [type_env after collect_expr_constraints]");
1031 for (expr_id, constraints) in &info.type_env.expr_constraints {
1032 for c in constraints {
1033 eprintln!(" expr_id={:?}: {} ({})", expr_id, c.ty.to_display_string(interner), c.context);
1034 }
1035 }
1036 eprintln!(" [param_constraints]");
1037 for (param_id, constraints) in &info.type_env.param_constraints {
1038 for c in constraints {
1039 eprintln!(" param={}: {} ({})", interner.get(*param_id), c.ty.to_display_string(interner), c.context);
1040 }
1041 }
1042 eprintln!(" [param_to_exprs]");
1043 for (param, expr_ids) in &info.type_env.param_to_exprs {
1044 eprintln!(" param={}: {:?}", interner.get(*param), expr_ids);
1045 }
1046 }
1047
1048 if let Some(apidoc_dict) = apidoc {
1050 let macro_name_str = interner.get(name);
1051 if let Some(entry) = apidoc_dict.get(macro_name_str) {
1052 if let Some(ref return_type) = entry.return_type {
1053 let mut type_repr = TypeRepr::from_c_type_string(return_type, interner, files, typedefs);
1054 if is_return_override {
1058 if let TypeRepr::CType { ref mut source, .. } = type_repr {
1059 *source = crate::type_repr::CTypeSource::PatchOverride {
1060 raw: return_type.clone(),
1061 };
1062 }
1063 }
1064 info.type_env.add_return_constraint(TypeConstraint::new(
1065 expr.id,
1066 type_repr,
1067 format!("return type of macro {}", macro_name_str),
1068 ));
1069 }
1070
1071 collect_generic_params(entry, info);
1073
1074 collect_literal_string_params(entry, info);
1076 }
1077 }
1078 }
1079
1080 if let ParseResult::Statement(ref block_items) = info.parse_result {
1082 let mut analyzer = SemanticAnalyzer::with_rust_decl_dict(
1083 interner,
1084 apidoc,
1085 fields_dict,
1086 rust_decl_dict,
1087 inline_fn_dict,
1088 );
1089
1090 analyzer.set_macro_return_types(return_types_cache);
1092
1093 analyzer.set_macro_param_types(param_types_cache);
1095
1096 analyzer.register_macro_params_from_apidoc(name, params, files, typedefs);
1098
1099 for item in block_items {
1101 if let BlockItem::Stmt(stmt) = item {
1102 analyzer.collect_stmt_constraints(stmt, &mut info.type_env);
1103 }
1104 }
1105
1106 if is_debug {
1108 eprintln!(" [type_env after collect_stmt_constraints]");
1109 for (expr_id, constraints) in &info.type_env.expr_constraints {
1111 for c in constraints {
1112 eprintln!(" expr_id={:?}: {} ({})", expr_id, c.ty.to_display_string(interner), c.context);
1113 }
1114 }
1115 eprintln!(" [param_constraints]");
1116 for (param_id, constraints) in &info.type_env.param_constraints {
1117 for c in constraints {
1118 eprintln!(" param={}: {} ({})", interner.get(*param_id), c.ty.to_display_string(interner), c.context);
1119 }
1120 }
1121 eprintln!(" [param_to_exprs]");
1122 for (param, expr_ids) in &info.type_env.param_to_exprs {
1123 eprintln!(" param={}: {:?}", interner.get(*param), expr_ids);
1124 }
1125 }
1126 }
1127
1128 if let Some(apidoc_dict) = apidoc {
1134 if let Some(entry) = apidoc_dict.get(macro_name_str) {
1135 if let Some(info) = self.macros.get_mut(&name) {
1136 let decls: Vec<(InternedStr, crate::ast::ExprId, String)> = info
1137 .params
1138 .iter()
1139 .enumerate()
1140 .filter_map(|(i, mp)| {
1141 entry
1142 .args
1143 .get(i)
1144 .filter(|a| !a.ty.is_empty())
1145 .map(|a| (mp.name, mp.expr_id(), a.ty.clone()))
1146 })
1147 .collect();
1148 for (pname, expr_id, ty_str) in decls {
1149 let mut tr =
1153 TypeRepr::from_c_type_string(&ty_str, interner, files, typedefs);
1154 if let TypeRepr::CType { source, .. } = &mut tr {
1155 *source = crate::type_repr::CTypeSource::Apidoc {
1156 raw: ty_str.clone(),
1157 };
1158 }
1159 if tr.is_void() {
1160 continue; }
1162 if is_debug {
1163 eprintln!(
1164 " [apidoc param decl] {}: {:?} (from {:?})",
1165 interner.get(pname), tr, ty_str
1166 );
1167 }
1168 info.type_env.add_param_constraint(
1169 pname,
1170 TypeConstraint::new(
1171 expr_id,
1172 tr,
1173 format!("apidoc param decl of {}", macro_name_str),
1174 ),
1175 );
1176 }
1177 }
1178 }
1179 }
1180 }
1181
1182 pub fn get_macro_return_type(&self, name: InternedStr, interner: &StringInterner) -> Option<(String, String)> {
1184 self.macros.get(&name).and_then(|info| {
1185 info.get_return_type().map(|ty| {
1186 (interner.get(name).to_string(), ty.to_rust_string(interner))
1187 })
1188 })
1189 }
1190
1191 fn collect_uses_from_called(
1196 &self,
1197 called_macros: &HashSet<InternedStr>,
1198 info: &mut MacroInferInfo,
1199 ) {
1200 for &id in called_macros {
1201 if id != info.name {
1202 info.add_use(id);
1203 }
1204 }
1205 }
1206
1207 fn has_toplevel_semicolon(tokens: &[Token]) -> bool {
1209 let mut depth = 0;
1210 for t in tokens {
1211 match t.kind {
1212 TokenKind::LParen | TokenKind::LBrace | TokenKind::LBracket => depth += 1,
1213 TokenKind::RParen | TokenKind::RBrace | TokenKind::RBracket => {
1214 if depth > 0 { depth -= 1; }
1215 }
1216 TokenKind::Semi if depth == 0 => return true,
1217 _ => {}
1218 }
1219 }
1220 false
1221 }
1222
1223 fn strip_empty_stmts(items: Vec<BlockItem>) -> Vec<BlockItem> {
1227 use crate::ast::Stmt;
1228 items
1229 .into_iter()
1230 .filter(|it| !matches!(it, BlockItem::Stmt(Stmt::Expr(None, _))))
1231 .collect()
1232 }
1233
1234 fn try_parse_tokens(
1239 &self,
1240 tokens: &[crate::token::Token],
1241 interner: &StringInterner,
1242 files: &FileRegistry,
1243 typedefs: &HashSet<InternedStr>,
1244 generic_params: HashMap<InternedStr, usize>,
1245 ) -> (ParseResult, ParseStats, HashSet<InternedStr>) {
1246 if tokens.is_empty() {
1247 return (ParseResult::Unparseable(Some("empty token sequence".to_string())), ParseStats::default(), HashSet::new());
1248 }
1249
1250 let first_significant = tokens.iter().find(|t| {
1252 !matches!(t.kind, TokenKind::Space | TokenKind::Newline)
1253 });
1254
1255 let is_statement_start = first_significant
1257 .is_some_and(|t| matches!(t.kind, TokenKind::KwDo | TokenKind::KwIf));
1258 if is_statement_start {
1259 if generic_params.is_empty() {
1260 match parse_statement_from_tokens_ref_with_stats(tokens.to_vec(), interner, files, typedefs) {
1261 Ok((stmt, stats)) => {
1262 return (
1263 ParseResult::Statement(vec![BlockItem::Stmt(stmt)]),
1264 stats,
1265 HashSet::new(),
1266 );
1267 }
1268 Err(_) => {} }
1270 } else {
1271 match parse_statement_from_tokens_ref_with_generic_params(tokens.to_vec(), interner, files, typedefs, generic_params.clone()) {
1272 Ok((stmt, stats, detected)) => {
1273 return (
1274 ParseResult::Statement(vec![BlockItem::Stmt(stmt)]),
1275 stats,
1276 detected,
1277 );
1278 }
1279 Err(_) => {} }
1281 }
1282 }
1283
1284 if Self::has_toplevel_semicolon(tokens) {
1286 if generic_params.is_empty() {
1287 match parse_block_items_from_tokens_ref_with_stats(tokens.to_vec(), interner, files, typedefs) {
1288 Ok((items, stats)) => {
1289 return (
1290 ParseResult::Statement(Self::strip_empty_stmts(items)),
1291 stats,
1292 HashSet::new(),
1293 );
1294 }
1295 Err(_) => {} }
1297 } else {
1298 match parse_block_items_from_tokens_ref_with_generic_params(tokens.to_vec(), interner, files, typedefs, generic_params.clone()) {
1299 Ok((items, stats, detected)) => {
1300 return (
1301 ParseResult::Statement(Self::strip_empty_stmts(items)),
1302 stats,
1303 detected,
1304 );
1305 }
1306 Err(_) => {} }
1308 }
1309 }
1310
1311 if generic_params.is_empty() {
1313 match parse_expression_from_tokens_ref_with_stats(tokens.to_vec(), interner, files, typedefs) {
1314 Ok((expr, stats)) => (
1315 ParseResult::Expression(Box::new(expr)),
1316 stats,
1317 HashSet::new(),
1318 ),
1319 Err(err) => (ParseResult::Unparseable(Some(err.format_with_files(files))), ParseStats::default(), HashSet::new()),
1320 }
1321 } else {
1322 match parse_expression_from_tokens_ref_with_generic_params(tokens.to_vec(), interner, files, typedefs, generic_params) {
1323 Ok((expr, stats, detected)) => (
1324 ParseResult::Expression(Box::new(expr)),
1325 stats,
1326 detected,
1327 ),
1328 Err(err) => (ParseResult::Unparseable(Some(err.format_with_files(files))), ParseStats::default(), HashSet::new()),
1329 }
1330 }
1331 }
1332
1333 pub fn analyze_all_macros<'a>(
1338 &mut self,
1339 pp: &mut Preprocessor,
1340 apidoc: Option<&'a ApidocDict>,
1341 apidoc_patches: Option<&'a ApidocPatchSet>,
1342 fields_dict: Option<&'a FieldsDict>,
1343 rust_decl_dict: Option<&'a RustDeclDict>,
1344 mut inline_fn_dict: Option<&'a mut InlineFnDict>,
1345 c_fn_decl_dict: Option<&'a CFnDeclDict>,
1346 typedefs: &HashSet<InternedStr>,
1347 thx_symbols: (InternedStr, InternedStr, InternedStr),
1348 no_expand: NoExpandSymbols,
1349 perl_build_mode: crate::perl_config::PerlBuildMode,
1350 ) {
1351 let mut thx_initial = HashSet::new();
1353 let mut pasting_initial = HashSet::new();
1354
1355 let target_macros: Vec<MacroDef> = pp.macros().iter_target_macros().cloned().collect();
1357
1358 for def in &target_macros {
1359 let (mut info, has_pasting, has_thx) = self.build_macro_info(
1360 def, pp, typedefs, thx_symbols, no_expand, perl_build_mode
1361 );
1362 info.local_usage = Some(crate::local_usage::analyze_macro(
1365 &info.parse_result, &info.params));
1366 if has_pasting {
1367 pasting_initial.insert(def.name);
1368 }
1369 if has_thx {
1370 thx_initial.insert(def.name);
1371 }
1372 self.register(info);
1373 }
1374
1375 if let Some(c_fn_dict) = c_fn_decl_dict {
1377 for (name, info) in &self.macros {
1378 let has_thx_from_fn_calls = info.called_functions.iter().any(|fn_name| {
1380 c_fn_dict.is_thx_dependent(*fn_name)
1381 });
1382 if has_thx_from_fn_calls && !thx_initial.contains(name) {
1383 thx_initial.insert(*name);
1384 }
1385 }
1386 }
1387
1388 if let Some(ref mut ifd) = inline_fn_dict {
1391 ifd.analyze_local_usage();
1392 }
1393
1394 self.build_use_relations();
1396
1397 self.propagate_flag_via_used_by(&thx_initial, true);
1399
1400 self.propagate_flag_via_used_by(&pasting_initial, false);
1402
1403 if let Some(patches) = apidoc_patches {
1407 let interner = pp.interner();
1408 self.return_override_names = patches
1410 .return_overrides
1411 .keys()
1412 .filter_map(|n| interner.lookup(n))
1413 .collect();
1414 let macro_hits = self.apply_apidoc_suppressions(patches, interner);
1415 let inline_hits = inline_fn_dict
1416 .as_mut()
1417 .map(|ifd| ifd.apply_apidoc_suppressions(patches, interner))
1418 .unwrap_or(0);
1419 let total = patches.skip_codegen.len();
1420 let unmatched = total.saturating_sub(macro_hits + inline_hits);
1421 eprintln!(
1422 "[apidoc-suppress] skip_codegen reflected: {} macro(s) + {} inline fn(s); \
1423 {} of {} entries unmatched (no such macro/inline; possibly stale skip-list)",
1424 macro_hits, inline_hits, unmatched, total,
1425 );
1426 }
1427
1428 if let Some(apidoc_dict) = apidoc {
1434 let interner = pp.interner();
1435 let mut pair_names: Vec<&str> = Vec::new();
1436 for (name, info) in self.macros.iter_mut() {
1437 let name_str = interner.get(*name);
1438 if let Some(entry) = apidoc_dict.get(name_str) {
1439 if entry.return_type.as_deref() == Some("pair") {
1440 info.pair_return = true;
1441 pair_names.push(name_str);
1442 }
1443 }
1444 }
1445 if !pair_names.is_empty() {
1446 eprintln!(
1447 "[apidoc-suppress] pair-return macro(s) excluded from codegen: {}",
1448 pair_names.join(", "),
1449 );
1450 }
1451 }
1452
1453 {
1455 let interner = pp.interner();
1456 self.check_function_availability(
1457 rust_decl_dict,
1458 inline_fn_dict.as_deref(),
1459 interner,
1460 );
1461 }
1462
1463 if let Some(ref mut ifd) = inline_fn_dict {
1465 let interner = pp.interner();
1466 self.check_inline_fn_availability(ifd, rust_decl_dict, interner);
1467 }
1468
1469 if let Some(ref mut ifd) = inline_fn_dict {
1471 self.propagate_unavailable_cross_domain(ifd);
1472 } else {
1473 self.propagate_unavailable_via_used_by();
1474 }
1475
1476 for name in self.macros.keys().copied().collect::<Vec<_>>() {
1478 self.unconfirmed.insert(name);
1479 }
1480
1481 {
1483 let macro_table = pp.macros();
1484 let interner = pp.interner();
1485 let files = pp.files();
1486 self.infer_types_in_dependency_order(
1487 macro_table, interner, files, apidoc, fields_dict, rust_decl_dict,
1488 inline_fn_dict.as_deref(), typedefs
1489 );
1490 }
1491 }
1492
1493 fn propagate_flag_via_used_by(&mut self, initial_set: &HashSet<InternedStr>, is_thx: bool) {
1497 for name in initial_set {
1499 if let Some(info) = self.macros.get_mut(name) {
1500 if is_thx {
1501 info.is_thx_dependent = true;
1502 } else {
1503 info.has_token_pasting = true;
1504 }
1505 }
1506 }
1507
1508 let mut to_propagate: Vec<InternedStr> = initial_set.iter().copied().collect();
1510
1511 while let Some(name) = to_propagate.pop() {
1512 let used_by_list: Vec<InternedStr> = self.macros
1513 .get(&name)
1514 .map(|info| info.used_by.iter().copied().collect())
1515 .unwrap_or_default();
1516
1517 for user in used_by_list {
1518 if let Some(user_info) = self.macros.get_mut(&user) {
1519 let flag = if is_thx {
1520 &mut user_info.is_thx_dependent
1521 } else {
1522 &mut user_info.has_token_pasting
1523 };
1524 if !*flag {
1525 *flag = true;
1526 to_propagate.push(user);
1527 }
1528 }
1529 }
1530 }
1531 }
1532
1533 fn check_function_availability(
1538 &mut self,
1539 rust_decl_dict: Option<&RustDeclDict>,
1540 inline_fn_dict: Option<&InlineFnDict>,
1541 interner: &StringInterner,
1542 ) {
1543 let bindings_fns: std::collections::HashSet<&str> = rust_decl_dict
1545 .map(|d| d.fns.keys().map(|s| s.as_str()).collect())
1546 .unwrap_or_default();
1547
1548 let builtin_fns: std::collections::HashSet<&str> = [
1550 "__builtin_expect",
1551 "__builtin_offsetof",
1552 "offsetof",
1553 "__builtin_types_compatible_p",
1554 "__builtin_constant_p",
1555 "__builtin_choose_expr",
1556 "__builtin_unreachable",
1557 "__builtin_trap",
1558 "__builtin_assume",
1559 "__builtin_bswap16",
1560 "__builtin_bswap32",
1561 "__builtin_bswap64",
1562 "__builtin_popcount",
1563 "__builtin_clz",
1564 "__builtin_ctz",
1565 "pthread_mutex_lock",
1566 "pthread_mutex_unlock",
1567 "pthread_rwlock_rdlock",
1568 "pthread_rwlock_wrlock",
1569 "pthread_rwlock_unlock",
1570 "memchr",
1571 "memcpy",
1572 "memmove",
1573 "memset",
1574 "strlen",
1575 "strcmp",
1576 "strncmp",
1577 "strcpy",
1578 "strncpy",
1579 "ASSERT_IS_LITERAL",
1580 "ASSERT_IS_PTR",
1581 "ASSERT_NOT_PTR",
1582 ].into_iter().collect();
1583
1584 let macro_names: HashSet<InternedStr> = self.macros.keys().copied().collect();
1586
1587 let macro_names_list: Vec<InternedStr> = self.macros.keys().copied().collect();
1589 for name in macro_names_list {
1590 let called_functions: Vec<InternedStr> = self.macros
1591 .get(&name)
1592 .map(|info| info.called_functions.iter().copied().collect())
1593 .unwrap_or_default();
1594
1595 let mut has_unavailable = false;
1596 for called_fn in called_functions {
1597 let fn_name = interner.get(called_fn);
1598
1599 if self.macros.get(&called_fn).is_some_and(|i| i.pair_return) {
1602 has_unavailable = true;
1603 break;
1604 }
1605
1606 if macro_names.contains(&called_fn) {
1608 continue;
1609 }
1610
1611 if bindings_fns.contains(fn_name) {
1613 continue;
1614 }
1615
1616 if let Some(inline_fns) = inline_fn_dict {
1618 if inline_fns.get(called_fn).is_some() {
1619 continue;
1620 }
1621 }
1622
1623 if builtin_fns.contains(fn_name) {
1625 continue;
1626 }
1627
1628 has_unavailable = true;
1630 break;
1631 }
1632
1633 if has_unavailable {
1634 if let Some(info) = self.macros.get_mut(&name) {
1635 info.calls_unavailable = true;
1636 }
1637 }
1638 }
1639 }
1640
1641 fn propagate_unavailable_via_used_by(&mut self) {
1648 let initial_set: HashSet<InternedStr> = self.macros
1650 .iter()
1651 .filter(|(_, info)| info.is_unavailable_for_codegen())
1652 .map(|(name, _)| *name)
1653 .collect();
1654
1655 let mut to_propagate: Vec<InternedStr> = initial_set.into_iter().collect();
1657
1658 while let Some(name) = to_propagate.pop() {
1659 let used_by_list: Vec<InternedStr> = self.macros
1660 .get(&name)
1661 .map(|info| info.used_by.iter().copied().collect())
1662 .unwrap_or_default();
1663
1664 for user in used_by_list {
1665 if let Some(user_info) = self.macros.get_mut(&user) {
1666 if !user_info.calls_unavailable {
1667 user_info.calls_unavailable = true;
1668 to_propagate.push(user);
1669 }
1670 }
1671 }
1672 }
1673 }
1674
1675 fn check_inline_fn_availability(
1680 &self,
1681 inline_fn_dict: &mut InlineFnDict,
1682 rust_decl_dict: Option<&RustDeclDict>,
1683 interner: &StringInterner,
1684 ) {
1685 let bindings_fns: HashSet<&str> = rust_decl_dict
1687 .map(|d| d.fns.keys().map(|s| s.as_str()).collect())
1688 .unwrap_or_default();
1689
1690 let builtin_fns: HashSet<&str> = [
1692 "__builtin_expect",
1693 "__builtin_offsetof",
1694 "offsetof",
1695 "__builtin_types_compatible_p",
1696 "__builtin_constant_p",
1697 "__builtin_choose_expr",
1698 "__builtin_unreachable",
1699 "__builtin_trap",
1700 "__builtin_assume",
1701 "__builtin_bswap16",
1702 "__builtin_bswap32",
1703 "__builtin_bswap64",
1704 "__builtin_popcount",
1705 "__builtin_clz",
1706 "__builtin_ctz",
1707 "pthread_mutex_lock",
1708 "pthread_mutex_unlock",
1709 "pthread_rwlock_rdlock",
1710 "pthread_rwlock_wrlock",
1711 "pthread_rwlock_unlock",
1712 "memchr",
1713 "memcpy",
1714 "memmove",
1715 "memset",
1716 "strlen",
1717 "strcmp",
1718 "strncmp",
1719 "strcpy",
1720 "strncpy",
1721 "ASSERT_IS_LITERAL",
1722 "ASSERT_IS_PTR",
1723 "ASSERT_NOT_PTR",
1724 ].into_iter().collect();
1725
1726 let macro_names: HashSet<InternedStr> = self.macros.keys().copied().collect();
1728
1729 let entries: Vec<(InternedStr, Vec<InternedStr>)> = inline_fn_dict
1731 .called_functions_iter()
1732 .map(|(name, calls)| (*name, calls.iter().copied().collect()))
1733 .collect();
1734
1735 for (name, called_fns) in entries {
1736 let mut has_unavailable = false;
1737 for called_fn in called_fns {
1738 let fn_name = interner.get(called_fn);
1739
1740 if self.macros.get(&called_fn).is_some_and(|i| i.pair_return) {
1743 has_unavailable = true;
1744 break;
1745 }
1746
1747 if macro_names.contains(&called_fn) { continue; }
1748 if bindings_fns.contains(fn_name) { continue; }
1749 if inline_fn_dict.get(called_fn).is_some() { continue; }
1750 if builtin_fns.contains(fn_name) { continue; }
1751
1752 has_unavailable = true;
1753 break;
1754 }
1755
1756 if has_unavailable {
1757 inline_fn_dict.set_calls_unavailable(name);
1758 }
1759 }
1760 }
1761
1762 fn propagate_unavailable_cross_domain(
1772 &mut self,
1773 inline_fn_dict: &mut InlineFnDict,
1774 ) {
1775 loop {
1776 let mut changed = false;
1777
1778 let macro_names: Vec<InternedStr> = self.macros.keys().copied().collect();
1780 for name in ¯o_names {
1781 if !self.macros.get(name)
1782 .map(|i| i.is_unavailable_for_codegen())
1783 .unwrap_or(false)
1784 {
1785 continue;
1786 }
1787 let used_by_list: Vec<InternedStr> = self.macros
1788 .get(name)
1789 .map(|info| info.used_by.iter().copied().collect())
1790 .unwrap_or_default();
1791 for user in used_by_list {
1792 if let Some(user_info) = self.macros.get_mut(&user) {
1793 if !user_info.calls_unavailable {
1794 user_info.calls_unavailable = true;
1795 changed = true;
1796 }
1797 }
1798 }
1799 }
1800
1801 let inline_entries: Vec<(InternedStr, Vec<InternedStr>)> = inline_fn_dict
1804 .called_functions_iter()
1805 .map(|(name, calls)| (*name, calls.iter().copied().collect()))
1806 .collect();
1807 for (name, calls) in &inline_entries {
1808 if inline_fn_dict.is_calls_unavailable(*name) {
1809 continue;
1810 }
1811 let has_unavailable_inline = calls.iter().any(|called| {
1812 inline_fn_dict.get(*called).is_some()
1813 && inline_fn_dict.is_unavailable_for_codegen(*called)
1814 });
1815 if has_unavailable_inline {
1816 inline_fn_dict.set_calls_unavailable(*name);
1817 changed = true;
1818 }
1819 }
1820
1821 for name in ¯o_names {
1824 if self.macros.get(name)
1825 .map(|i| i.calls_unavailable)
1826 .unwrap_or(false)
1827 {
1828 continue;
1829 }
1830 let called_fns: Vec<InternedStr> = self.macros
1831 .get(name)
1832 .map(|info| info.called_functions.iter().copied().collect())
1833 .unwrap_or_default();
1834 let has_unavailable_inline = called_fns.iter().any(|called| {
1835 inline_fn_dict.get(*called).is_some()
1836 && inline_fn_dict.is_unavailable_for_codegen(*called)
1837 });
1838 if has_unavailable_inline {
1839 if let Some(info) = self.macros.get_mut(name) {
1840 info.calls_unavailable = true;
1841 changed = true;
1842 }
1843 }
1844 }
1845
1846 for (name, calls) in &inline_entries {
1849 if inline_fn_dict.is_calls_unavailable(*name) {
1850 continue;
1851 }
1852 let has_unavailable_macro = calls.iter().any(|called| {
1853 self.macros.get(called)
1854 .map(|info| info.is_unavailable_for_codegen())
1855 .unwrap_or(false)
1856 });
1857 if has_unavailable_macro {
1858 inline_fn_dict.set_calls_unavailable(*name);
1859 changed = true;
1860 }
1861 }
1862
1863 if !changed {
1864 break;
1865 }
1866 }
1867 }
1868
1869 fn infer_types_in_dependency_order<'a>(
1871 &mut self,
1872 macro_table: &MacroTable,
1873 interner: &'a StringInterner,
1874 files: &FileRegistry,
1875 apidoc: Option<&'a ApidocDict>,
1876 fields_dict: Option<&'a FieldsDict>,
1877 rust_decl_dict: Option<&'a RustDeclDict>,
1878 inline_fn_dict: Option<&'a InlineFnDict>,
1879 typedefs: &HashSet<InternedStr>,
1880 ) {
1881 let mut return_types_cache: HashMap<String, String> = HashMap::new();
1883 let mut param_types_cache: HashMap<String, Vec<(String, String)>> = HashMap::new();
1885
1886 loop {
1887 let candidates = self.get_inference_candidates();
1888 if candidates.is_empty() {
1889 let mut remaining: Vec<_> = self.unconfirmed.iter().copied().collect();
1892 remaining.sort();
1893 for name in remaining {
1894 let params: Vec<InternedStr> = macro_table
1896 .get(name)
1897 .map(|def| match &def.kind {
1898 MacroKind::Function { params, .. } => params.clone(),
1899 MacroKind::Object => vec![],
1900 })
1901 .unwrap_or_default();
1902
1903 self.infer_macro_types(
1905 name, ¶ms, interner, files, apidoc, fields_dict, rust_decl_dict, inline_fn_dict, typedefs,
1906 &return_types_cache, ¶m_types_cache,
1907 );
1908
1909 let is_confirmed = self.macros.get(&name)
1914 .map(|info| {
1915 !info.stmt_unrepresentable
1916 && (info.get_return_type().is_some()
1917 || info.is_statement_with_resolvable_params())
1918 })
1919 .unwrap_or(false);
1920
1921 if is_confirmed {
1922 if let Some((macro_name, return_type)) = self.get_macro_return_type(name, interner) {
1923 return_types_cache.insert(macro_name, return_type);
1924 }
1925 self.mark_confirmed(name);
1926 self.cache_param_types_to(name, interner, &mut param_types_cache);
1927 } else {
1928 self.move_to_unknown(name);
1929 }
1930 }
1931 break;
1932 }
1933
1934 for name in candidates {
1935 let params: Vec<InternedStr> = macro_table
1937 .get(name)
1938 .map(|def| match &def.kind {
1939 MacroKind::Function { params, .. } => params.clone(),
1940 MacroKind::Object => vec![],
1941 })
1942 .unwrap_or_default();
1943
1944 self.infer_macro_types(
1946 name, ¶ms, interner, files, apidoc, fields_dict, rust_decl_dict, inline_fn_dict, typedefs,
1947 &return_types_cache, ¶m_types_cache,
1948 );
1949
1950 let is_confirmed = self.macros.get(&name)
1952 .map(|info| {
1953 !info.stmt_unrepresentable
1959 && (info.get_return_type().is_some()
1960 || info.is_statement_with_resolvable_params())
1961 })
1962 .unwrap_or(false);
1963
1964 if is_confirmed {
1965 if let Some((macro_name, return_type)) = self.get_macro_return_type(name, interner) {
1967 return_types_cache.insert(macro_name, return_type);
1968 }
1969 self.mark_confirmed(name);
1970 self.cache_param_types_to(name, interner, &mut param_types_cache);
1971 } else {
1972 self.move_to_unknown(name);
1973 }
1974 }
1975 }
1976
1977 self.macro_param_types = param_types_cache;
1979 }
1980
1981 pub fn expr_uses_typedef_as_value(expr: &Expr, typedefs: &HashSet<InternedStr>) -> bool {
1986 match &expr.kind {
1987 ExprKind::Ident(name) => typedefs.contains(name),
1988 ExprKind::Call { func, args } => {
1989 let func_hit = match &func.kind {
1991 ExprKind::Ident(_) => false,
1992 _ => Self::expr_uses_typedef_as_value(func, typedefs),
1993 };
1994 func_hit || args.iter().any(|a| Self::expr_uses_typedef_as_value(a, typedefs))
1995 }
1996 ExprKind::Binary { lhs, rhs, .. }
1997 | ExprKind::Assign { lhs, rhs, .. }
1998 | ExprKind::Comma { lhs, rhs } => {
1999 Self::expr_uses_typedef_as_value(lhs, typedefs)
2000 || Self::expr_uses_typedef_as_value(rhs, typedefs)
2001 }
2002 ExprKind::Cast { expr: inner, .. }
2003 | ExprKind::PreInc(inner)
2004 | ExprKind::PreDec(inner)
2005 | ExprKind::PostInc(inner)
2006 | ExprKind::PostDec(inner)
2007 | ExprKind::AddrOf(inner)
2008 | ExprKind::Deref(inner)
2009 | ExprKind::UnaryPlus(inner)
2010 | ExprKind::UnaryMinus(inner)
2011 | ExprKind::BitNot(inner)
2012 | ExprKind::LogNot(inner) => Self::expr_uses_typedef_as_value(inner, typedefs),
2013 ExprKind::Index { expr: base, index } => {
2014 Self::expr_uses_typedef_as_value(base, typedefs)
2015 || Self::expr_uses_typedef_as_value(index, typedefs)
2016 }
2017 ExprKind::Member { expr: base, .. } | ExprKind::PtrMember { expr: base, .. } => {
2018 Self::expr_uses_typedef_as_value(base, typedefs)
2019 }
2020 ExprKind::Conditional { cond, then_expr, else_expr } => {
2021 Self::expr_uses_typedef_as_value(cond, typedefs)
2022 || Self::expr_uses_typedef_as_value(then_expr, typedefs)
2023 || Self::expr_uses_typedef_as_value(else_expr, typedefs)
2024 }
2025 _ => false,
2026 }
2027 }
2028
2029 pub fn collect_uses_from_expr(
2031 expr: &Expr,
2032 uses: &mut HashSet<InternedStr>,
2033 ) {
2034 match &expr.kind {
2035 ExprKind::Call { func, args } => {
2036 if let ExprKind::Ident(name) = &func.kind {
2038 uses.insert(*name);
2039 }
2040 Self::collect_uses_from_expr(func, uses);
2041 for arg in args {
2042 Self::collect_uses_from_expr(arg, uses);
2043 }
2044 }
2045 ExprKind::Ident(name) => {
2046 uses.insert(*name);
2047 }
2048 ExprKind::Binary { lhs, rhs, .. } => {
2049 Self::collect_uses_from_expr(lhs, uses);
2050 Self::collect_uses_from_expr(rhs, uses);
2051 }
2052 ExprKind::Cast { expr: inner, .. }
2053 | ExprKind::PreInc(inner)
2054 | ExprKind::PreDec(inner)
2055 | ExprKind::PostInc(inner)
2056 | ExprKind::PostDec(inner)
2057 | ExprKind::AddrOf(inner)
2058 | ExprKind::Deref(inner)
2059 | ExprKind::UnaryPlus(inner)
2060 | ExprKind::UnaryMinus(inner)
2061 | ExprKind::BitNot(inner)
2062 | ExprKind::LogNot(inner)
2063 | ExprKind::Sizeof(inner) => {
2064 Self::collect_uses_from_expr(inner, uses);
2065 }
2066 ExprKind::Index { expr: base, index } => {
2067 Self::collect_uses_from_expr(base, uses);
2068 Self::collect_uses_from_expr(index, uses);
2069 }
2070 ExprKind::Member { expr: base, .. } | ExprKind::PtrMember { expr: base, .. } => {
2071 Self::collect_uses_from_expr(base, uses);
2072 }
2073 ExprKind::Conditional { cond, then_expr, else_expr } => {
2074 Self::collect_uses_from_expr(cond, uses);
2075 Self::collect_uses_from_expr(then_expr, uses);
2076 Self::collect_uses_from_expr(else_expr, uses);
2077 }
2078 ExprKind::Assign { lhs, rhs, .. } => {
2079 Self::collect_uses_from_expr(lhs, uses);
2080 Self::collect_uses_from_expr(rhs, uses);
2081 }
2082 ExprKind::Comma { lhs, rhs } => {
2083 Self::collect_uses_from_expr(lhs, uses);
2084 Self::collect_uses_from_expr(rhs, uses);
2085 }
2086 ExprKind::BuiltinCall { args, .. } => {
2087 for arg in args {
2088 if let crate::ast::BuiltinArg::Expr(e) = arg {
2089 Self::collect_uses_from_expr(e, uses);
2090 }
2091 }
2092 }
2093 ExprKind::Assert { condition, .. } => {
2094 Self::collect_uses_from_expr(condition, uses);
2095 }
2096 _ => {}
2097 }
2098 }
2099
2100 pub fn collect_function_calls_from_expr(
2102 expr: &Expr,
2103 calls: &mut HashSet<InternedStr>,
2104 ) {
2105 match &expr.kind {
2106 ExprKind::Call { func, args } => {
2107 if let ExprKind::Ident(name) = &func.kind {
2109 calls.insert(*name);
2110 }
2111 Self::collect_function_calls_from_expr(func, calls);
2112 for arg in args {
2113 Self::collect_function_calls_from_expr(arg, calls);
2114 }
2115 }
2116 ExprKind::Binary { lhs, rhs, .. } => {
2117 Self::collect_function_calls_from_expr(lhs, calls);
2118 Self::collect_function_calls_from_expr(rhs, calls);
2119 }
2120 ExprKind::Cast { expr: inner, .. }
2121 | ExprKind::PreInc(inner)
2122 | ExprKind::PreDec(inner)
2123 | ExprKind::PostInc(inner)
2124 | ExprKind::PostDec(inner)
2125 | ExprKind::AddrOf(inner)
2126 | ExprKind::Deref(inner)
2127 | ExprKind::UnaryPlus(inner)
2128 | ExprKind::UnaryMinus(inner)
2129 | ExprKind::BitNot(inner)
2130 | ExprKind::LogNot(inner)
2131 | ExprKind::Sizeof(inner) => {
2132 Self::collect_function_calls_from_expr(inner, calls);
2133 }
2134 ExprKind::Index { expr: base, index } => {
2135 Self::collect_function_calls_from_expr(base, calls);
2136 Self::collect_function_calls_from_expr(index, calls);
2137 }
2138 ExprKind::Member { expr: base, .. } | ExprKind::PtrMember { expr: base, .. } => {
2139 Self::collect_function_calls_from_expr(base, calls);
2140 }
2141 ExprKind::Conditional { cond, then_expr, else_expr } => {
2142 Self::collect_function_calls_from_expr(cond, calls);
2143 Self::collect_function_calls_from_expr(then_expr, calls);
2144 Self::collect_function_calls_from_expr(else_expr, calls);
2145 }
2146 ExprKind::Assign { lhs, rhs, .. } => {
2147 Self::collect_function_calls_from_expr(lhs, calls);
2148 Self::collect_function_calls_from_expr(rhs, calls);
2149 }
2150 ExprKind::Comma { lhs, rhs } => {
2151 Self::collect_function_calls_from_expr(lhs, calls);
2152 Self::collect_function_calls_from_expr(rhs, calls);
2153 }
2154 ExprKind::StmtExpr(compound) => {
2155 Self::collect_function_calls_from_block_items(&compound.items, calls);
2156 }
2157 ExprKind::BuiltinCall { args, .. } => {
2158 for arg in args {
2159 if let crate::ast::BuiltinArg::Expr(e) = arg {
2160 Self::collect_function_calls_from_expr(e, calls);
2161 }
2162 }
2163 }
2164 ExprKind::Assert { condition, .. } => {
2165 Self::collect_function_calls_from_expr(condition, calls);
2166 }
2167 _ => {}
2168 }
2169 }
2170
2171 pub fn collect_function_calls_from_block_items(
2173 items: &[BlockItem],
2174 calls: &mut HashSet<InternedStr>,
2175 ) {
2176 for item in items {
2177 match item {
2178 BlockItem::Stmt(stmt) => {
2179 Self::collect_function_calls_from_stmt(stmt, calls);
2180 }
2181 BlockItem::Decl(decl) => {
2182 Self::collect_function_calls_from_decl(decl, calls);
2183 }
2184 }
2185 }
2186 }
2187
2188 fn collect_function_calls_from_decl(
2190 decl: &crate::ast::Declaration,
2191 calls: &mut HashSet<InternedStr>,
2192 ) {
2193 for init_decl in &decl.declarators {
2194 if let Some(init) = &init_decl.init {
2195 Self::collect_function_calls_from_initializer(init, calls);
2196 }
2197 }
2198 }
2199
2200 fn collect_function_calls_from_initializer(
2202 init: &crate::ast::Initializer,
2203 calls: &mut HashSet<InternedStr>,
2204 ) {
2205 match init {
2206 crate::ast::Initializer::Expr(expr) => {
2207 Self::collect_function_calls_from_expr(expr, calls);
2208 }
2209 crate::ast::Initializer::List(items) => {
2210 for item in items {
2211 Self::collect_function_calls_from_initializer(&item.init, calls);
2212 }
2213 }
2214 }
2215 }
2216
2217 fn collect_function_calls_from_stmt(
2219 stmt: &crate::ast::Stmt,
2220 calls: &mut HashSet<InternedStr>,
2221 ) {
2222 use crate::ast::{Stmt, ForInit};
2223 match stmt {
2224 Stmt::Expr(Some(expr), _) => {
2225 Self::collect_function_calls_from_expr(expr, calls);
2226 }
2227 Stmt::If { cond, then_stmt, else_stmt, .. } => {
2228 Self::collect_function_calls_from_expr(cond, calls);
2229 Self::collect_function_calls_from_stmt(then_stmt, calls);
2230 if let Some(else_s) = else_stmt {
2231 Self::collect_function_calls_from_stmt(else_s, calls);
2232 }
2233 }
2234 Stmt::While { cond, body, .. } => {
2235 Self::collect_function_calls_from_expr(cond, calls);
2236 Self::collect_function_calls_from_stmt(body, calls);
2237 }
2238 Stmt::DoWhile { body, cond, .. } => {
2239 Self::collect_function_calls_from_stmt(body, calls);
2240 Self::collect_function_calls_from_expr(cond, calls);
2241 }
2242 Stmt::For { init, cond, step, body, .. } => {
2243 if let Some(for_init) = init {
2244 match for_init {
2245 ForInit::Expr(expr) => {
2246 Self::collect_function_calls_from_expr(expr, calls);
2247 }
2248 ForInit::Decl(_) => {
2249 }
2251 }
2252 }
2253 if let Some(cond_expr) = cond {
2254 Self::collect_function_calls_from_expr(cond_expr, calls);
2255 }
2256 if let Some(step_expr) = step {
2257 Self::collect_function_calls_from_expr(step_expr, calls);
2258 }
2259 Self::collect_function_calls_from_stmt(body, calls);
2260 }
2261 Stmt::Compound(compound) => {
2262 Self::collect_function_calls_from_block_items(&compound.items, calls);
2263 }
2264 Stmt::Return(Some(expr), _) => {
2265 Self::collect_function_calls_from_expr(expr, calls);
2266 }
2267 Stmt::Switch { expr, body, .. } => {
2268 Self::collect_function_calls_from_expr(expr, calls);
2269 Self::collect_function_calls_from_stmt(body, calls);
2270 }
2271 Stmt::Label { stmt, .. } | Stmt::Case { stmt, .. } | Stmt::Default { stmt, .. } => {
2272 Self::collect_function_calls_from_stmt(stmt, calls);
2273 }
2274 _ => {}
2275 }
2276 }
2277
2278 pub fn resolve_param_and_return_types(
2285 &mut self,
2286 interner: &mut StringInterner,
2287 rust_decl_dict: Option<&crate::rust_decl::RustDeclDict>,
2288 inline_fn_dict: &crate::inline_fn::InlineFnDict,
2289 ) {
2290 self.propagate_macro_return_types(interner);
2298
2299 let sorted = self.topological_sort_for_resolve();
2301
2302 let mut callee_const_params: HashMap<InternedStr, HashSet<usize>> = HashMap::new();
2304 Self::seed_callee_const(interner, rust_decl_dict, inline_fn_dict, &mut callee_const_params);
2305
2306 let mut bool_return_set: HashSet<InternedStr> = HashSet::new();
2308 Self::seed_bool_returns(interner, rust_decl_dict, inline_fn_dict, &mut bool_return_set);
2309
2310 for name in &sorted {
2312 let info = match self.macros.get(name) {
2313 Some(info) => info,
2314 None => continue,
2315 };
2316 if !info.is_parseable() || info.calls_unavailable || !info.is_function {
2317 continue;
2318 }
2319
2320 let must_mut = crate::rust_codegen::collect_must_mut_pointer_params(
2322 &info.parse_result,
2323 &info.params,
2324 &callee_const_params,
2325 );
2326 let mut const_positions = HashSet::new();
2327 for (i, param) in info.params.iter().enumerate() {
2328 if !must_mut.contains(¶m.name) {
2329 if Self::param_has_pointer_type_static(&info.type_env, param) {
2331 const_positions.insert(i);
2332 }
2333 }
2334 }
2335 if !const_positions.is_empty() {
2336 callee_const_params.insert(*name, const_positions.clone());
2337 }
2338
2339 let is_bool = if let ParseResult::Expression(expr) = &info.parse_result {
2341 crate::rust_codegen::is_boolean_expr_with_context(
2342 expr, &bool_return_set, &bool_return_set,
2343 )
2344 } else {
2345 false
2346 };
2347 if is_bool {
2348 bool_return_set.insert(*name);
2349 }
2350
2351 let info_mut = self.macros.get_mut(name).unwrap();
2353 info_mut.const_pointer_positions = const_positions;
2354 info_mut.is_bool_return = is_bool;
2355 }
2356 }
2357
2358 fn propagate_macro_return_types(&mut self, _interner: &StringInterner) {
2370 let sorted = self.topological_sort_for_resolve();
2371 let mut macro_returns: HashMap<InternedStr, TypeRepr> = HashMap::new();
2372
2373 for name in &sorted {
2374 let computed: Option<(crate::ast::ExprId, TypeRepr)> = {
2377 let info = match self.macros.get(name) {
2378 Some(i) => i,
2379 None => continue,
2380 };
2381 if !info.is_parseable() || info.calls_unavailable || !info.is_function {
2382 continue;
2383 }
2384 let ParseResult::Expression(ref expr) = info.parse_result else {
2385 continue;
2386 };
2387 compute_macro_return_type(expr, &info.type_env, ¯o_returns)
2388 .map(|ty| (expr.id, ty))
2389 };
2390
2391 if let Some((expr_id, ret_ty)) = computed {
2392 macro_returns.insert(*name, ret_ty.clone());
2393 let info_mut = self.macros.get_mut(name).unwrap();
2394 info_mut.type_env.add_return_constraint(TypeConstraint::new(
2395 expr_id,
2396 ret_ty,
2397 "macro return propagated from callee macros",
2398 ));
2399 }
2400
2401 let updates: Vec<(crate::ast::ExprId, TypeRepr)> = {
2412 let info = self.macros.get(name).unwrap();
2413 let mut acc = Vec::new();
2414 collect_macro_call_updates(&info.parse_result, ¯o_returns, &mut acc);
2415 acc
2416 };
2417 if !updates.is_empty() {
2418 let info_mut = self.macros.get_mut(name).unwrap();
2419 for (eid, ty) in updates {
2420 if !ty.is_concrete_pointer() {
2421 continue;
2422 }
2423 if let Some(cs) = info_mut.type_env.expr_constraints.get_mut(&eid) {
2424 let mut should_replace = false;
2425 cs.retain(|c| {
2426 let stale = c.context.starts_with("return type of macro ")
2427 && c.ty.is_void_pointer();
2428 if stale {
2429 should_replace = true;
2430 false
2431 } else {
2432 true
2433 }
2434 });
2435 if should_replace {
2436 cs.push(TypeConstraint::new(
2437 eid,
2438 ty,
2439 "return type from propagated callee macro (void* override)",
2440 ));
2441 }
2442 }
2443 }
2444 }
2445 }
2446 }
2447
2448 fn topological_sort_for_resolve(&self) -> Vec<InternedStr> {
2450 use std::collections::VecDeque;
2451 let target_macros: HashSet<InternedStr> = self.macros.iter()
2452 .filter(|(_, info)| info.is_target && info.has_body && info.is_function)
2453 .map(|(n, _)| *n)
2454 .collect();
2455
2456 let mut in_degree: HashMap<InternedStr, usize> = HashMap::new();
2457 for &name in &target_macros {
2458 in_degree.entry(name).or_insert(0);
2459 if let Some(info) = self.macros.get(&name) {
2460 for used in &info.uses {
2461 if target_macros.contains(used) {
2462 *in_degree.entry(name).or_insert(0) += 1;
2463 }
2464 }
2465 }
2466 }
2467
2468 let mut queue: VecDeque<InternedStr> = in_degree.iter()
2469 .filter(|(_, deg)| **deg == 0)
2470 .map(|(&name, _)| name)
2471 .collect();
2472 let mut result = Vec::new();
2473 while let Some(name) = queue.pop_front() {
2474 result.push(name);
2475 if let Some(info) = self.macros.get(&name) {
2476 for user in &info.used_by {
2477 if let Some(deg) = in_degree.get_mut(user) {
2478 *deg = deg.saturating_sub(1);
2479 if *deg == 0 {
2480 queue.push_back(*user);
2481 }
2482 }
2483 }
2484 }
2485 }
2486 for &name in &target_macros {
2488 if !result.contains(&name) {
2489 result.push(name);
2490 }
2491 }
2492 result
2493 }
2494
2495 fn param_has_pointer_type_static(type_env: &crate::type_env::TypeEnv, param: &MacroParam) -> bool {
2497 if let Some(expr_ids) = type_env.param_to_exprs.get(¶m.name) {
2498 for expr_id in expr_ids {
2499 if let Some(constraints) = type_env.expr_constraints.get(expr_id) {
2500 for c in constraints {
2501 if c.ty.has_outer_pointer() {
2502 return true;
2503 }
2504 }
2505 }
2506 }
2507 }
2508 let expr_id = param.expr_id();
2509 if let Some(constraints) = type_env.expr_constraints.get(&expr_id) {
2510 for c in constraints {
2511 if c.ty.has_outer_pointer() {
2512 return true;
2513 }
2514 }
2515 }
2516 false
2517 }
2518
2519 fn seed_callee_const(
2521 interner: &mut StringInterner,
2522 rust_decl_dict: Option<&crate::rust_decl::RustDeclDict>,
2523 inline_fn_dict: &crate::inline_fn::InlineFnDict,
2524 callee_const: &mut HashMap<InternedStr, HashSet<usize>>,
2525 ) {
2526 if let Some(dict) = rust_decl_dict {
2527 for (name, func) in &dict.fns {
2528 let name_id = interner.intern(name);
2529 let mut positions = HashSet::new();
2530 for (i, param) in func.params.iter().enumerate() {
2531 let normalized = param.ty.replace(" ", "");
2533 if normalized.contains("*const") {
2534 positions.insert(i);
2535 }
2536 }
2537 if !positions.is_empty() {
2538 callee_const.insert(name_id, positions);
2539 }
2540 }
2541 }
2542 for (name_id, fn_info) in inline_fn_dict.iter() {
2543 let mut positions = HashSet::new();
2544 for dd in &fn_info.declarator.derived {
2545 if let crate::ast::DerivedDecl::Function(param_list) = dd {
2546 for (i, param) in param_list.params.iter().enumerate() {
2547 if let Some(ref decl) = param.declarator {
2548 let pointer_count = decl.derived.iter().filter(|d| {
2549 matches!(d, crate::ast::DerivedDecl::Pointer(_))
2550 }).count();
2551 let has_const = (pointer_count == 1
2562 && param.specs.qualifiers.is_const)
2563 || decl.derived.iter().any(|d| {
2564 matches!(d, crate::ast::DerivedDecl::Pointer(q) if q.is_const)
2565 });
2566 if has_const && pointer_count > 0 {
2567 positions.insert(i);
2568 }
2569 }
2570 }
2571 break;
2572 }
2573 }
2574 if !positions.is_empty() {
2575 callee_const.insert(*name_id, positions);
2576 }
2577 }
2578 }
2579
2580 fn seed_bool_returns(
2582 interner: &mut StringInterner,
2583 rust_decl_dict: Option<&crate::rust_decl::RustDeclDict>,
2584 inline_fn_dict: &crate::inline_fn::InlineFnDict,
2585 bool_returns: &mut HashSet<InternedStr>,
2586 ) {
2587 if let Some(dict) = rust_decl_dict {
2588 for (name, func) in &dict.fns {
2589 if func.ret_ty.as_deref() == Some("bool") {
2590 let name_id = interner.intern(name);
2591 bool_returns.insert(name_id);
2592 }
2593 }
2594 }
2595 for (name_id, fn_info) in inline_fn_dict.iter() {
2596 let has_bool = fn_info.specs.type_specs.iter()
2597 .any(|ts| matches!(ts, crate::ast::TypeSpec::Bool));
2598 if has_bool {
2599 bool_returns.insert(*name_id);
2600 }
2601 }
2602 }
2603}
2604
2605impl Default for MacroInferContext {
2606 fn default() -> Self {
2607 Self::new()
2608 }
2609}
2610
2611fn collect_macro_call_updates(
2622 parse_result: &ParseResult,
2623 macro_returns: &HashMap<InternedStr, TypeRepr>,
2624 acc: &mut Vec<(crate::ast::ExprId, TypeRepr)>,
2625) {
2626 match parse_result {
2627 ParseResult::Expression(e) => visit_expr_for_calls(e, macro_returns, acc),
2628 ParseResult::Statement(items) => {
2629 for it in items {
2630 if let BlockItem::Stmt(stmt) = it {
2631 visit_stmt_for_calls(stmt, macro_returns, acc);
2632 }
2633 }
2634 }
2635 ParseResult::Unparseable(_) => {}
2636 }
2637}
2638
2639fn visit_expr_for_calls(
2640 expr: &Expr,
2641 macro_returns: &HashMap<InternedStr, TypeRepr>,
2642 acc: &mut Vec<(crate::ast::ExprId, TypeRepr)>,
2643) {
2644 if let ExprKind::Call { func, args } = &expr.kind {
2645 if let ExprKind::Ident(callee) = &func.kind {
2646 if let Some(ty) = macro_returns.get(callee) {
2647 acc.push((expr.id, ty.clone()));
2648 }
2649 }
2650 visit_expr_for_calls(func, macro_returns, acc);
2651 for a in args {
2652 visit_expr_for_calls(a, macro_returns, acc);
2653 }
2654 return;
2655 }
2656 walk_expr_children(expr, &mut |e| visit_expr_for_calls(e, macro_returns, acc));
2657}
2658
2659fn visit_stmt_for_calls(
2660 stmt: &crate::ast::Stmt,
2661 macro_returns: &HashMap<InternedStr, TypeRepr>,
2662 acc: &mut Vec<(crate::ast::ExprId, TypeRepr)>,
2663) {
2664 use crate::ast::Stmt;
2665 match stmt {
2666 Stmt::Compound(c) => {
2667 for it in &c.items {
2668 if let BlockItem::Stmt(s) = it {
2669 visit_stmt_for_calls(s, macro_returns, acc);
2670 }
2671 }
2672 }
2673 Stmt::Expr(Some(e), _) | Stmt::Return(Some(e), _) => {
2674 visit_expr_for_calls(e, macro_returns, acc)
2675 }
2676 Stmt::If { cond, then_stmt, else_stmt, .. } => {
2677 visit_expr_for_calls(cond, macro_returns, acc);
2678 visit_stmt_for_calls(then_stmt, macro_returns, acc);
2679 if let Some(es) = else_stmt {
2680 visit_stmt_for_calls(es, macro_returns, acc);
2681 }
2682 }
2683 Stmt::While { cond, body, .. } | Stmt::DoWhile { body, cond, .. } => {
2684 visit_expr_for_calls(cond, macro_returns, acc);
2685 visit_stmt_for_calls(body, macro_returns, acc);
2686 }
2687 Stmt::For { init, cond, step, body, .. } => {
2688 if let Some(crate::ast::ForInit::Expr(e)) = init {
2689 visit_expr_for_calls(e, macro_returns, acc);
2690 }
2691 if let Some(c) = cond {
2692 visit_expr_for_calls(c, macro_returns, acc);
2693 }
2694 if let Some(s) = step {
2695 visit_expr_for_calls(s, macro_returns, acc);
2696 }
2697 visit_stmt_for_calls(body, macro_returns, acc);
2698 }
2699 Stmt::Switch { expr, body, .. } => {
2700 visit_expr_for_calls(expr, macro_returns, acc);
2701 visit_stmt_for_calls(body, macro_returns, acc);
2702 }
2703 Stmt::Case { expr, stmt, .. } => {
2704 visit_expr_for_calls(expr, macro_returns, acc);
2705 visit_stmt_for_calls(stmt, macro_returns, acc);
2706 }
2707 Stmt::Default { stmt, .. } | Stmt::Label { stmt, .. } => {
2708 visit_stmt_for_calls(stmt, macro_returns, acc);
2709 }
2710 _ => {}
2711 }
2712}
2713
2714fn walk_expr_children<F: FnMut(&Expr)>(expr: &Expr, f: &mut F) {
2716 match &expr.kind {
2717 ExprKind::Ident(_)
2718 | ExprKind::IntLit(_)
2719 | ExprKind::UIntLit(_)
2720 | ExprKind::FloatLit(_)
2721 | ExprKind::CharLit(_)
2722 | ExprKind::StringLit(_)
2723 | ExprKind::SizeofType(_)
2724 | ExprKind::Alignof(_) => {}
2725 ExprKind::Call { func, args } => {
2726 f(func);
2727 for a in args { f(a); }
2728 }
2729 ExprKind::Index { expr: e, index } => { f(e); f(index); }
2730 ExprKind::Member { expr: e, .. } | ExprKind::PtrMember { expr: e, .. } => f(e),
2731 ExprKind::PostInc(e)
2732 | ExprKind::PostDec(e)
2733 | ExprKind::PreInc(e)
2734 | ExprKind::PreDec(e)
2735 | ExprKind::AddrOf(e)
2736 | ExprKind::Deref(e)
2737 | ExprKind::UnaryPlus(e)
2738 | ExprKind::UnaryMinus(e)
2739 | ExprKind::BitNot(e)
2740 | ExprKind::LogNot(e)
2741 | ExprKind::Sizeof(e) => f(e),
2742 ExprKind::Cast { expr: e, .. } => f(e),
2743 ExprKind::Binary { lhs, rhs, .. } => { f(lhs); f(rhs); }
2744 ExprKind::Assign { lhs, rhs, .. } => { f(lhs); f(rhs); }
2745 ExprKind::Conditional { cond, then_expr, else_expr } => {
2746 f(cond); f(then_expr); f(else_expr);
2747 }
2748 ExprKind::Comma { lhs, rhs } => { f(lhs); f(rhs); }
2749 ExprKind::CompoundLit { .. } => {}
2750 ExprKind::BuiltinCall { args, .. } => {
2751 for a in args {
2752 if let crate::ast::BuiltinArg::Expr(e) = a { f(e); }
2753 }
2754 }
2755 ExprKind::StmtExpr(_) => {}
2756 ExprKind::Assert { condition, .. } => f(condition),
2757 ExprKind::MacroCall { args, expanded, .. } => {
2758 for a in args { f(a); }
2759 f(expanded);
2760 }
2761 }
2762}
2763
2764fn compute_macro_return_type(
2765 expr: &Expr,
2766 env: &TypeEnv,
2767 macro_returns: &HashMap<InternedStr, TypeRepr>,
2768) -> Option<TypeRepr> {
2769 match &expr.kind {
2770 ExprKind::Conditional { then_expr, else_expr, .. } => {
2771 let then_ty = compute_macro_return_type(then_expr, env, macro_returns)
2772 .or_else(|| existing_constraint_type(then_expr.id, env));
2773 let else_ty = compute_macro_return_type(else_expr, env, macro_returns)
2774 .or_else(|| existing_constraint_type(else_expr.id, env));
2775 resolve_conditional_branches(then_ty, else_ty)
2776 }
2777 ExprKind::Call { func, .. } => {
2778 if let ExprKind::Ident(callee_name) = &func.kind {
2779 if let Some(ty) = macro_returns.get(callee_name) {
2780 return Some(ty.clone());
2781 }
2782 }
2783 existing_constraint_type(expr.id, env)
2784 }
2785 ExprKind::Comma { rhs, .. } => {
2787 compute_macro_return_type(rhs, env, macro_returns)
2788 .or_else(|| existing_constraint_type(rhs.id, env))
2789 }
2790 ExprKind::Binary { op, lhs, rhs } => {
2795 let lhs_ty = compute_macro_return_type(lhs, env, macro_returns)
2796 .or_else(|| existing_constraint_type(lhs.id, env));
2797 let rhs_ty = compute_macro_return_type(rhs, env, macro_returns)
2798 .or_else(|| existing_constraint_type(rhs.id, env));
2799 resolve_binary(op, lhs_ty, rhs_ty)
2800 }
2801 ExprKind::Cast { .. } => existing_constraint_type(expr.id, env),
2803 _ => existing_constraint_type(expr.id, env),
2805 }
2806}
2807
2808fn existing_constraint_type(
2810 expr_id: crate::ast::ExprId,
2811 env: &TypeEnv,
2812) -> Option<TypeRepr> {
2813 env.expr_constraints
2814 .get(&expr_id)
2815 .and_then(|cs| cs.first())
2816 .map(|c| c.ty.clone())
2817}
2818
2819fn resolve_binary(
2825 op: &crate::ast::BinOp,
2826 lhs_ty: Option<TypeRepr>,
2827 rhs_ty: Option<TypeRepr>,
2828) -> Option<TypeRepr> {
2829 use crate::ast::BinOp;
2830 match op {
2831 BinOp::Add | BinOp::Sub => {
2832 let lhs_is_ptr = lhs_ty.as_ref().is_some_and(|t| t.is_pointer_type());
2833 let rhs_is_ptr = rhs_ty.as_ref().is_some_and(|t| t.is_pointer_type());
2834 match (lhs_is_ptr, rhs_is_ptr) {
2835 (true, false) => lhs_ty,
2836 (false, true) => rhs_ty,
2837 _ => None, }
2839 }
2840 _ => None,
2841 }
2842}
2843
2844fn resolve_conditional_branches(
2848 then_ty: Option<TypeRepr>,
2849 else_ty: Option<TypeRepr>,
2850) -> Option<TypeRepr> {
2851 match (&then_ty, &else_ty) {
2852 (Some(t), Some(e)) => {
2853 if t.is_void_pointer() && e.is_concrete_pointer() {
2854 return else_ty;
2855 }
2856 if e.is_void_pointer() && t.is_concrete_pointer() {
2857 return then_ty;
2858 }
2859 then_ty
2863 }
2864 (Some(_), None) => then_ty,
2865 (None, Some(_)) => else_ty,
2866 (None, None) => None,
2867 }
2868}
2869
2870fn inject_comma_after_assert_underscore(
2877 tokens: &[Token],
2878 no_expand: &NoExpandSymbols,
2879) -> Vec<Token> {
2880 let assert_underscore = no_expand.assert_;
2881
2882 let mut result = Vec::with_capacity(tokens.len());
2883 let mut i = 0;
2884
2885 while i < tokens.len() {
2886 if matches!(tokens[i].kind, TokenKind::Ident(name) if name == assert_underscore) {
2887 result.push(tokens[i].clone());
2889 i += 1;
2890
2891 while i < tokens.len() && matches!(tokens[i].kind, TokenKind::Space | TokenKind::Newline) {
2893 result.push(tokens[i].clone());
2894 i += 1;
2895 }
2896
2897 if i < tokens.len() && matches!(tokens[i].kind, TokenKind::LParen) {
2899 let mut depth = 0;
2900 loop {
2901 if i >= tokens.len() {
2902 break;
2903 }
2904 match tokens[i].kind {
2905 TokenKind::LParen => depth += 1,
2906 TokenKind::RParen => {
2907 depth -= 1;
2908 if depth == 0 {
2909 result.push(tokens[i].clone());
2910 i += 1;
2911 break;
2912 }
2913 }
2914 _ => {}
2915 }
2916 result.push(tokens[i].clone());
2917 i += 1;
2918 }
2919
2920 let next_significant = tokens[i..].iter()
2922 .find(|t| !matches!(t.kind, TokenKind::Space | TokenKind::Newline));
2923 let needs_comma = next_significant
2924 .is_some_and(|t| !matches!(t.kind,
2925 TokenKind::Comma | TokenKind::RParen | TokenKind::Eof
2926 | TokenKind::Semi));
2927 if needs_comma {
2928 let loc = result.last().map(|t| t.loc.clone())
2929 .unwrap_or_default();
2930 result.push(Token::new(TokenKind::Comma, loc));
2931 }
2932 }
2933 } else {
2934 result.push(tokens[i].clone());
2935 i += 1;
2936 }
2937 }
2938
2939 result
2940}
2941
2942pub fn detect_assert_kind(name: &str) -> Option<AssertKind> {
2944 match name {
2945 "assert" => Some(AssertKind::Assert),
2946 "assert_" => Some(AssertKind::AssertUnderscore),
2947 _ => None,
2948 }
2949}
2950
2951pub fn convert_assert_calls(expr: &mut Expr, interner: &StringInterner) {
2956 match &mut expr.kind {
2957 ExprKind::Call { func, args } => {
2958 convert_assert_calls(func, interner);
2960 for arg in args.iter_mut() {
2961 convert_assert_calls(arg, interner);
2962 }
2963
2964 if let ExprKind::Ident(name) = &func.kind {
2966 let name_str = interner.get(*name);
2967 if let Some(kind) = detect_assert_kind(name_str) {
2968 if let Some(condition) = args.pop() {
2969 expr.kind = ExprKind::Assert {
2970 kind,
2971 condition: Box::new(condition),
2972 };
2973 }
2974 }
2975 }
2976 }
2977 ExprKind::Binary { lhs, rhs, .. } => {
2978 convert_assert_calls(lhs, interner);
2979 convert_assert_calls(rhs, interner);
2980 }
2981 ExprKind::Cast { expr: inner, .. }
2982 | ExprKind::PreInc(inner)
2983 | ExprKind::PreDec(inner)
2984 | ExprKind::PostInc(inner)
2985 | ExprKind::PostDec(inner)
2986 | ExprKind::AddrOf(inner)
2987 | ExprKind::Deref(inner)
2988 | ExprKind::UnaryPlus(inner)
2989 | ExprKind::UnaryMinus(inner)
2990 | ExprKind::BitNot(inner)
2991 | ExprKind::LogNot(inner)
2992 | ExprKind::Sizeof(inner) => {
2993 convert_assert_calls(inner, interner);
2994 }
2995 ExprKind::Index { expr: base, index } => {
2996 convert_assert_calls(base, interner);
2997 convert_assert_calls(index, interner);
2998 }
2999 ExprKind::Member { expr: base, .. } | ExprKind::PtrMember { expr: base, .. } => {
3000 convert_assert_calls(base, interner);
3001 }
3002 ExprKind::Conditional { cond, then_expr, else_expr } => {
3003 convert_assert_calls(cond, interner);
3004 convert_assert_calls(then_expr, interner);
3005 convert_assert_calls(else_expr, interner);
3006 }
3007 ExprKind::Assign { lhs, rhs, .. } => {
3008 convert_assert_calls(lhs, interner);
3009 convert_assert_calls(rhs, interner);
3010 }
3011 ExprKind::Comma { lhs, rhs } => {
3012 convert_assert_calls(lhs, interner);
3013 convert_assert_calls(rhs, interner);
3014 }
3015 ExprKind::Assert { condition, .. } => {
3016 convert_assert_calls(condition, interner);
3017 }
3018 ExprKind::CompoundLit { init, .. } => {
3019 for item in init {
3020 if let crate::ast::Initializer::Expr(e) = &mut item.init {
3021 convert_assert_calls(e, interner);
3022 }
3023 }
3024 }
3025 ExprKind::StmtExpr(compound) => {
3026 for item in &mut compound.items {
3027 if let BlockItem::Stmt(stmt) = item {
3028 convert_assert_calls_in_stmt(stmt, interner);
3029 }
3030 }
3031 }
3032 ExprKind::MacroCall { args, expanded, .. } => {
3034 for arg in args.iter_mut() {
3035 convert_assert_calls(arg, interner);
3036 }
3037 convert_assert_calls(expanded, interner);
3038 }
3039 ExprKind::BuiltinCall { args, .. } => {
3040 for arg in args.iter_mut() {
3041 if let crate::ast::BuiltinArg::Expr(e) = arg {
3042 convert_assert_calls(e, interner);
3043 }
3044 }
3045 }
3046 ExprKind::Ident(_)
3048 | ExprKind::IntLit(_)
3049 | ExprKind::UIntLit(_)
3050 | ExprKind::FloatLit(_)
3051 | ExprKind::CharLit(_)
3052 | ExprKind::StringLit(_)
3053 | ExprKind::SizeofType(_)
3054 | ExprKind::Alignof(_) => {}
3055 }
3056}
3057
3058pub fn convert_assert_calls_in_compound_stmt(compound: &mut crate::ast::CompoundStmt, interner: &StringInterner) {
3062 use crate::ast::BlockItem;
3063 for item in &mut compound.items {
3064 if let BlockItem::Stmt(s) = item {
3065 convert_assert_calls_in_stmt(s, interner);
3066 }
3067 }
3068}
3069
3070pub fn convert_assert_calls_in_stmt(stmt: &mut crate::ast::Stmt, interner: &StringInterner) {
3072 use crate::ast::Stmt;
3073 match stmt {
3074 Stmt::Expr(Some(expr), _) => convert_assert_calls(expr, interner),
3075 Stmt::If { cond, then_stmt, else_stmt, .. } => {
3076 convert_assert_calls(cond, interner);
3077 convert_assert_calls_in_stmt(then_stmt, interner);
3078 if let Some(else_s) = else_stmt {
3079 convert_assert_calls_in_stmt(else_s, interner);
3080 }
3081 }
3082 Stmt::While { cond, body, .. } => {
3083 convert_assert_calls(cond, interner);
3084 convert_assert_calls_in_stmt(body, interner);
3085 }
3086 Stmt::DoWhile { body, cond, .. } => {
3087 convert_assert_calls_in_stmt(body, interner);
3088 convert_assert_calls(cond, interner);
3089 }
3090 Stmt::For { init, cond, step, body, .. } => {
3091 if let Some(crate::ast::ForInit::Expr(e)) = init {
3092 convert_assert_calls(e, interner);
3093 }
3094 if let Some(c) = cond {
3095 convert_assert_calls(c, interner);
3096 }
3097 if let Some(s) = step {
3098 convert_assert_calls(s, interner);
3099 }
3100 convert_assert_calls_in_stmt(body, interner);
3101 }
3102 Stmt::Switch { expr, body, .. } => {
3103 convert_assert_calls(expr, interner);
3104 convert_assert_calls_in_stmt(body, interner);
3105 }
3106 Stmt::Return(Some(expr), _) => convert_assert_calls(expr, interner),
3107 Stmt::Compound(compound) => {
3108 for item in &mut compound.items {
3109 match item {
3110 BlockItem::Stmt(s) => convert_assert_calls_in_stmt(s, interner),
3111 BlockItem::Decl(_) => {}
3112 }
3113 }
3114 }
3115 Stmt::Label { stmt: s, .. }
3116 | Stmt::Case { stmt: s, .. }
3117 | Stmt::Default { stmt: s, .. } => {
3118 convert_assert_calls_in_stmt(s, interner);
3119 }
3120 _ => {}
3121 }
3122}
3123
3124#[derive(Debug, Clone, Copy)]
3126pub struct MacroInferStats {
3127 pub total: usize,
3128 pub confirmed: usize,
3129 pub unconfirmed: usize,
3130 pub args_unknown: usize,
3132 pub return_unknown: usize,
3134}
3135
3136impl std::fmt::Display for MacroInferStats {
3137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3138 write!(
3139 f,
3140 "MacroInferStats {{ total: {}, confirmed: {}, unconfirmed: {}, args_unknown: {}, return_unknown: {} }}",
3141 self.total, self.confirmed, self.unconfirmed, self.args_unknown, self.return_unknown
3142 )
3143 }
3144}
3145
3146#[cfg(test)]
3147mod tests {
3148 use super::*;
3149 use crate::intern::StringInterner;
3150
3151 #[test]
3152 fn test_macro_infer_info_new() {
3153 let mut interner = StringInterner::new();
3154 let name = interner.intern("MY_MACRO");
3155
3156 let info = MacroInferInfo::new(name);
3157
3158 assert_eq!(info.name, name);
3159 assert!(!info.is_target);
3160 assert!(!info.is_thx_dependent);
3161 assert!(!info.has_token_pasting);
3162 assert!(info.uses.is_empty());
3163 assert!(info.used_by.is_empty());
3164 assert!(!info.is_parseable());
3165 assert_eq!(info.args_infer_status, InferStatus::Pending);
3166 assert_eq!(info.return_infer_status, InferStatus::Pending);
3167 }
3168
3169 #[test]
3170 fn test_macro_infer_context_register() {
3171 let mut interner = StringInterner::new();
3172 let name = interner.intern("FOO");
3173
3174 let mut ctx = MacroInferContext::new();
3175 let info = MacroInferInfo::new(name);
3176 ctx.register(info);
3177
3178 assert!(ctx.get(name).is_some());
3179 assert_eq!(ctx.macros.len(), 1);
3180 }
3181
3182 #[test]
3183 fn test_build_use_relations() {
3184 let mut interner = StringInterner::new();
3185 let foo = interner.intern("FOO");
3186 let bar = interner.intern("BAR");
3187 let baz = interner.intern("BAZ");
3188
3189 let mut ctx = MacroInferContext::new();
3190
3191 let mut foo_info = MacroInferInfo::new(foo);
3193 foo_info.add_use(bar);
3194 ctx.register(foo_info);
3195
3196 let mut bar_info = MacroInferInfo::new(bar);
3198 bar_info.add_use(baz);
3199 ctx.register(bar_info);
3200
3201 let baz_info = MacroInferInfo::new(baz);
3203 ctx.register(baz_info);
3204
3205 ctx.build_use_relations();
3207
3208 assert!(ctx.get(bar).unwrap().used_by.contains(&foo));
3210 assert!(ctx.get(baz).unwrap().used_by.contains(&bar));
3212 }
3213
3214 #[test]
3215 fn test_inference_candidates() {
3216 let mut interner = StringInterner::new();
3217 let foo = interner.intern("FOO");
3218 let bar = interner.intern("BAR");
3219 let baz = interner.intern("BAZ");
3220
3221 let mut ctx = MacroInferContext::new();
3222
3223 let mut foo_info = MacroInferInfo::new(foo);
3225 foo_info.add_use(bar);
3226 ctx.register(foo_info);
3227
3228 let mut bar_info = MacroInferInfo::new(bar);
3230 bar_info.add_use(baz);
3231 ctx.register(bar_info);
3232
3233 let mut baz_info = MacroInferInfo::new(baz);
3235 baz_info.args_infer_status = InferStatus::TypeComplete;
3236 baz_info.return_infer_status = InferStatus::TypeComplete;
3237 ctx.register(baz_info);
3238
3239 ctx.classify_initial();
3240
3241 assert!(ctx.confirmed.contains(&baz));
3243 assert!(ctx.unconfirmed.contains(&foo));
3244 assert!(ctx.unconfirmed.contains(&bar));
3245
3246 let candidates = ctx.get_inference_candidates();
3248 assert_eq!(candidates, vec![bar]);
3249
3250 ctx.mark_confirmed(bar);
3252 let candidates = ctx.get_inference_candidates();
3253 assert_eq!(candidates, vec![foo]);
3254 }
3255
3256 #[test]
3257 fn test_no_expand_symbols_new() {
3258 let mut interner = StringInterner::new();
3259 let symbols = NoExpandSymbols::new(&mut interner);
3260
3261 assert_eq!(interner.get(symbols.assert), "assert");
3262 assert_eq!(interner.get(symbols.assert_), "assert_");
3263 }
3264
3265 #[test]
3266 fn test_no_expand_symbols_iter() {
3267 let mut interner = StringInterner::new();
3268 let symbols = NoExpandSymbols::new(&mut interner);
3269
3270 let syms: Vec<_> = symbols.iter().collect();
3271 assert_eq!(syms.len(), 2);
3272 assert!(syms.contains(&symbols.assert));
3273 assert!(syms.contains(&symbols.assert_));
3274 }
3275
3276 #[test]
3277 fn test_explicit_expand_symbols_new() {
3278 let mut interner = StringInterner::new();
3279 let symbols = ExplicitExpandSymbols::new(&mut interner);
3280
3281 assert_eq!(interner.get(symbols.sv_any), "SvANY");
3282 assert_eq!(interner.get(symbols.sv_flags), "SvFLAGS");
3283 assert_eq!(interner.get(symbols.expect), "EXPECT");
3284 assert_eq!(interner.get(symbols.likely), "LIKELY");
3285 assert_eq!(interner.get(symbols.unlikely), "UNLIKELY");
3286 assert_eq!(interner.get(symbols.cbool), "cBOOL");
3287 assert_eq!(interner.get(symbols.assert_underscore_), "__ASSERT_");
3288 assert_eq!(interner.get(symbols.str_with_len), "STR_WITH_LEN");
3289 assert_eq!(interner.get(symbols.assert_not_rok), "assert_not_ROK");
3290 assert_eq!(interner.get(symbols.assert_not_glob), "assert_not_glob");
3291 assert_eq!(interner.get(symbols.mutable_ptr), "MUTABLE_PTR");
3292 }
3293
3294 #[test]
3295 fn test_explicit_expand_symbols_iter() {
3296 let mut interner = StringInterner::new();
3297 let symbols = ExplicitExpandSymbols::new(&mut interner);
3298
3299 let syms: Vec<_> = symbols.iter().collect();
3300 assert_eq!(syms.len(), 14);
3301 assert!(syms.contains(&symbols.assert_is_literal));
3302 assert!(syms.contains(&symbols.sv_any));
3303 assert!(syms.contains(&symbols.sv_flags));
3304 assert!(syms.contains(&symbols.cv_flags));
3305 assert!(syms.contains(&symbols.hek_flags));
3306 assert!(syms.contains(&symbols.expect));
3307 assert!(syms.contains(&symbols.likely));
3308 assert!(syms.contains(&symbols.unlikely));
3309 assert!(syms.contains(&symbols.cbool));
3310 assert!(syms.contains(&symbols.assert_underscore_));
3311 assert!(syms.contains(&symbols.str_with_len));
3312 assert!(syms.contains(&symbols.assert_not_rok));
3313 assert!(syms.contains(&symbols.assert_not_glob));
3314 assert!(!syms.contains(&symbols.mutable_ptr));
3318 }
3319}