1use super::comp::MethodKind;
4use super::context::{BindgenContext, TypeId};
5use super::dot::DotAttributes;
6use super::item::Item;
7use super::traversal::{EdgeKind, Trace, Tracer};
8use super::ty::TypeKind;
9use crate::callbacks::{ItemInfo, ItemKind};
10use crate::clang::{self, ABIKind, Attribute};
11use crate::parse::{ClangSubItemParser, ParseError, ParseResult};
12use ext_php_rs_clang_sys::CXCallingConv;
13
14use quote::TokenStreamExt;
15use std::io;
16use std::str::FromStr;
17
18const RUST_DERIVE_FUNPTR_LIMIT: usize = 12;
19
20#[derive(Debug, Copy, Clone, PartialEq, Eq)]
22pub(crate) enum FunctionKind {
23 Function,
25 Method(MethodKind),
27}
28
29impl FunctionKind {
30 pub(crate) fn from_cursor(cursor: &clang::Cursor) -> Option<FunctionKind> {
33 Some(match cursor.kind() {
35 ext_php_rs_clang_sys::CXCursor_FunctionDecl => {
36 FunctionKind::Function
37 }
38 ext_php_rs_clang_sys::CXCursor_Constructor => {
39 FunctionKind::Method(MethodKind::Constructor)
40 }
41 ext_php_rs_clang_sys::CXCursor_Destructor => {
42 FunctionKind::Method(if cursor.method_is_virtual() {
43 MethodKind::VirtualDestructor {
44 pure_virtual: cursor.method_is_pure_virtual(),
45 }
46 } else {
47 MethodKind::Destructor
48 })
49 }
50 ext_php_rs_clang_sys::CXCursor_CXXMethod => {
51 if cursor.method_is_virtual() {
52 FunctionKind::Method(MethodKind::Virtual {
53 pure_virtual: cursor.method_is_pure_virtual(),
54 })
55 } else if cursor.method_is_static() {
56 FunctionKind::Method(MethodKind::Static)
57 } else {
58 FunctionKind::Method(MethodKind::Normal)
59 }
60 }
61 _ => return None,
62 })
63 }
64}
65
66#[derive(Debug, Clone, Copy)]
68pub(crate) enum Linkage {
69 External,
71 Internal,
73}
74
75#[derive(Debug)]
80pub(crate) struct Function {
81 name: String,
83
84 mangled_name: Option<String>,
86
87 link_name: Option<String>,
89
90 signature: TypeId,
92
93 kind: FunctionKind,
95
96 linkage: Linkage,
98}
99
100impl Function {
101 pub(crate) fn new(
103 name: String,
104 mangled_name: Option<String>,
105 link_name: Option<String>,
106 signature: TypeId,
107 kind: FunctionKind,
108 linkage: Linkage,
109 ) -> Self {
110 Function {
111 name,
112 mangled_name,
113 link_name,
114 signature,
115 kind,
116 linkage,
117 }
118 }
119
120 pub(crate) fn name(&self) -> &str {
122 &self.name
123 }
124
125 pub(crate) fn mangled_name(&self) -> Option<&str> {
127 self.mangled_name.as_deref()
128 }
129
130 pub fn link_name(&self) -> Option<&str> {
132 self.link_name.as_deref()
133 }
134
135 pub(crate) fn signature(&self) -> TypeId {
137 self.signature
138 }
139
140 pub(crate) fn kind(&self) -> FunctionKind {
142 self.kind
143 }
144
145 pub(crate) fn linkage(&self) -> Linkage {
147 self.linkage
148 }
149}
150
151impl DotAttributes for Function {
152 fn dot_attributes<W>(
153 &self,
154 _ctx: &BindgenContext,
155 out: &mut W,
156 ) -> io::Result<()>
157 where
158 W: io::Write,
159 {
160 if let Some(ref mangled) = self.mangled_name {
161 let mangled: String =
162 mangled.chars().flat_map(|c| c.escape_default()).collect();
163 writeln!(out, "<tr><td>mangled name</td><td>{mangled}</td></tr>")?;
164 }
165
166 Ok(())
167 }
168}
169
170#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
172pub enum Abi {
173 C,
175 Stdcall,
177 EfiApi,
179 Fastcall,
181 ThisCall,
183 Vectorcall,
185 Aapcs,
187 Win64,
189 CUnwind,
191 System,
193}
194
195impl FromStr for Abi {
196 type Err = String;
197
198 fn from_str(s: &str) -> Result<Self, Self::Err> {
199 match s {
200 "C" => Ok(Self::C),
201 "stdcall" => Ok(Self::Stdcall),
202 "efiapi" => Ok(Self::EfiApi),
203 "fastcall" => Ok(Self::Fastcall),
204 "thiscall" => Ok(Self::ThisCall),
205 "vectorcall" => Ok(Self::Vectorcall),
206 "aapcs" => Ok(Self::Aapcs),
207 "win64" => Ok(Self::Win64),
208 "C-unwind" => Ok(Self::CUnwind),
209 "system" => Ok(Self::System),
210 _ => Err(format!("Invalid or unknown ABI {s:?}")),
211 }
212 }
213}
214
215impl std::fmt::Display for Abi {
216 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217 let s = match *self {
218 Self::C => "C",
219 Self::Stdcall => "stdcall",
220 Self::EfiApi => "efiapi",
221 Self::Fastcall => "fastcall",
222 Self::ThisCall => "thiscall",
223 Self::Vectorcall => "vectorcall",
224 Self::Aapcs => "aapcs",
225 Self::Win64 => "win64",
226 Self::CUnwind => "C-unwind",
227 Abi::System => "system",
228 };
229
230 s.fmt(f)
231 }
232}
233
234impl quote::ToTokens for Abi {
235 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
236 let abi = self.to_string();
237 tokens.append_all(quote! { #abi });
238 }
239}
240
241#[derive(Debug, Copy, Clone)]
243pub(crate) enum ClangAbi {
244 Known(Abi),
246 Unknown(CXCallingConv),
248}
249
250impl ClangAbi {
251 fn is_unknown(self) -> bool {
253 matches!(self, ClangAbi::Unknown(..))
254 }
255}
256
257impl quote::ToTokens for ClangAbi {
258 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
259 match *self {
260 Self::Known(abi) => abi.to_tokens(tokens),
261 Self::Unknown(cc) => panic!(
262 "Cannot turn unknown calling convention to tokens: {cc:?}"
263 ),
264 }
265 }
266}
267
268#[derive(Debug)]
270pub(crate) struct FunctionSig {
271 name: String,
273
274 return_type: TypeId,
276
277 argument_types: Vec<(Option<String>, TypeId)>,
280
281 is_variadic: bool,
283 is_divergent: bool,
284
285 must_use: bool,
287
288 abi: ClangAbi,
290}
291
292fn get_abi(cc: CXCallingConv) -> ClangAbi {
293 use ext_php_rs_clang_sys::*;
294 match cc {
295 cc if cc as u32 == 20 => ClangAbi::Known(Abi::C),
298
299 CXCallingConv_Default | CXCallingConv_C => ClangAbi::Known(Abi::C),
300 CXCallingConv_X86StdCall => ClangAbi::Known(Abi::Stdcall),
301 CXCallingConv_X86FastCall => ClangAbi::Known(Abi::Fastcall),
302 CXCallingConv_X86ThisCall => ClangAbi::Known(Abi::ThisCall),
303 CXCallingConv_X86VectorCall | CXCallingConv_AArch64VectorCall => {
304 ClangAbi::Known(Abi::Vectorcall)
305 }
306 CXCallingConv_AAPCS => ClangAbi::Known(Abi::Aapcs),
307 CXCallingConv_X86_64Win64 => ClangAbi::Known(Abi::Win64),
308 other => ClangAbi::Unknown(other),
309 }
310}
311
312pub(crate) fn cursor_mangling(
314 ctx: &BindgenContext,
315 cursor: &clang::Cursor,
316) -> Option<String> {
317 if !ctx.options().enable_mangling {
318 return None;
319 }
320
321 if cursor.is_in_non_fully_specialized_template() {
325 return None;
326 }
327
328 let is_itanium_abi = ctx.abi_kind() == ABIKind::GenericItanium;
329 let is_destructor =
330 cursor.kind() == ext_php_rs_clang_sys::CXCursor_Destructor;
331 if let Ok(mut manglings) = cursor.cxx_manglings() {
332 while let Some(m) = manglings.pop() {
333 if is_itanium_abi && is_destructor && !m.ends_with("D1Ev") {
335 continue;
336 }
337
338 return Some(m);
339 }
340 }
341
342 let mut mangling = cursor.mangling();
343 if mangling.is_empty() {
344 return None;
345 }
346
347 if is_itanium_abi && is_destructor {
348 if mangling.ends_with("D0Ev") {
368 let new_len = mangling.len() - 4;
369 mangling.truncate(new_len);
370 mangling.push_str("D1Ev");
371 }
372 }
373
374 Some(mangling)
375}
376
377fn args_from_ty_and_cursor(
378 ty: &clang::Type,
379 cursor: &clang::Cursor,
380 ctx: &mut BindgenContext,
381) -> Vec<(Option<String>, TypeId)> {
382 let cursor_args = cursor.args().unwrap_or_default().into_iter();
383 let type_args = ty.args().unwrap_or_default().into_iter();
384
385 cursor_args
395 .map(Some)
396 .chain(std::iter::repeat(None))
397 .zip(type_args.map(Some).chain(std::iter::repeat(None)))
398 .take_while(|(cur, ty)| cur.is_some() || ty.is_some())
399 .map(|(arg_cur, arg_ty)| {
400 let name = arg_cur.map(|a| a.spelling()).and_then(|name| {
401 if name.is_empty() {
402 None
403 } else {
404 Some(name)
405 }
406 });
407
408 let cursor = arg_cur.unwrap_or(*cursor);
409 let ty = arg_ty.unwrap_or_else(|| cursor.cur_type());
410 (name, Item::from_ty_or_ref(ty, cursor, None, ctx))
411 })
412 .collect()
413}
414
415impl FunctionSig {
416 pub(crate) fn name(&self) -> &str {
418 &self.name
419 }
420
421 pub(crate) fn from_ty(
423 ty: &clang::Type,
424 cursor: &clang::Cursor,
425 ctx: &mut BindgenContext,
426 ) -> Result<Self, ParseError> {
427 use ext_php_rs_clang_sys::*;
428 debug!("FunctionSig::from_ty {ty:?} {cursor:?}");
429
430 let kind = cursor.kind();
432 if kind == CXCursor_FunctionTemplate {
433 return Err(ParseError::Continue);
434 }
435
436 let spelling = cursor.spelling();
437
438 let is_operator = |spelling: &str| {
440 spelling.starts_with("operator")
441 && !clang::is_valid_identifier(spelling)
442 };
443 if is_operator(&spelling) && !ctx.options().represent_cxx_operators {
444 return Err(ParseError::Continue);
445 }
446
447 if (kind == CXCursor_Constructor || kind == CXCursor_Destructor)
451 && spelling.contains('<')
452 {
453 return Err(ParseError::Continue);
454 }
455
456 let cursor = if cursor.is_valid() {
457 *cursor
458 } else {
459 ty.declaration()
460 };
461
462 let mut args = match kind {
463 CXCursor_FunctionDecl
464 | CXCursor_Constructor
465 | CXCursor_CXXMethod
466 | CXCursor_ObjCInstanceMethodDecl
467 | CXCursor_ObjCClassMethodDecl => {
468 args_from_ty_and_cursor(ty, &cursor, ctx)
469 }
470 _ => {
471 let mut args = vec![];
474 cursor.visit(|c| {
475 if c.kind() == CXCursor_ParmDecl {
476 let ty =
477 Item::from_ty_or_ref(c.cur_type(), c, None, ctx);
478 let name = c.spelling();
479 let name =
480 if name.is_empty() { None } else { Some(name) };
481 args.push((name, ty));
482 }
483 CXChildVisit_Continue
484 });
485
486 if args.is_empty() {
487 args_from_ty_and_cursor(ty, &cursor, ctx)
492 } else {
493 args
494 }
495 }
496 };
497
498 let (must_use, mut is_divergent) =
499 if ctx.options().enable_function_attribute_detection {
500 let [must_use, no_return, no_return_cpp] = cursor.has_attrs(&[
501 Attribute::MUST_USE,
502 Attribute::NO_RETURN,
503 Attribute::NO_RETURN_CPP,
504 ]);
505 (must_use, no_return || no_return_cpp)
506 } else {
507 Default::default()
508 };
509
510 let ty_spelling = ty.spelling();
514 let has_attribute_noreturn = ty_spelling
515 .match_indices("__attribute__((noreturn))")
516 .any(|(i, _)| {
517 let depth = ty_spelling[..i]
518 .bytes()
519 .filter_map(|ch| match ch {
520 b'(' => Some(1),
521 b')' => Some(-1),
522 _ => None,
523 })
524 .sum::<isize>();
525 depth == 0
526 });
527 is_divergent = is_divergent || has_attribute_noreturn;
528
529 let is_method = kind == CXCursor_CXXMethod;
530 let is_constructor = kind == CXCursor_Constructor;
531 let is_destructor = kind == CXCursor_Destructor;
532 if (is_constructor || is_destructor || is_method)
533 && cursor.lexical_parent() != cursor.semantic_parent()
534 {
535 return Err(ParseError::Continue);
537 }
538
539 if is_method || is_constructor || is_destructor {
540 let is_const = is_method && cursor.method_is_const();
541 let is_virtual = is_method && cursor.method_is_virtual();
542 let is_static = is_method && cursor.method_is_static();
543 if !is_static
544 && (!is_virtual
545 || ctx.options().use_specific_virtual_function_receiver)
546 {
547 let parent = cursor.semantic_parent();
548 let class = Item::parse(parent, None, ctx)
549 .expect("Expected to parse the class");
550 let class = class.as_type_id_unchecked();
553
554 let class = if is_const {
555 let const_class_id = ctx.next_item_id();
556 ctx.build_const_wrapper(
557 const_class_id,
558 class,
559 None,
560 &parent.cur_type(),
561 )
562 } else {
563 class
564 };
565
566 let ptr =
567 Item::builtin_type(TypeKind::Pointer(class), false, ctx);
568 args.insert(0, (Some("this".into()), ptr));
569 } else if is_virtual {
570 let void = Item::builtin_type(TypeKind::Void, false, ctx);
571 let ptr =
572 Item::builtin_type(TypeKind::Pointer(void), false, ctx);
573 args.insert(0, (Some("this".into()), ptr));
574 }
575 }
576
577 let ty_ret_type = if kind == CXCursor_ObjCInstanceMethodDecl
578 || kind == CXCursor_ObjCClassMethodDecl
579 {
580 ty.ret_type()
581 .or_else(|| cursor.ret_type())
582 .ok_or(ParseError::Continue)?
583 } else {
584 ty.ret_type().ok_or(ParseError::Continue)?
585 };
586
587 let ret = if is_constructor && ctx.is_target_wasm32() {
588 let void = Item::builtin_type(TypeKind::Void, false, ctx);
591 Item::builtin_type(TypeKind::Pointer(void), false, ctx)
592 } else {
593 Item::from_ty_or_ref(ty_ret_type, cursor, None, ctx)
594 };
595
596 let mut call_conv = ty.call_conv();
599 if let Some(ty) = cursor.cur_type().canonical_type().pointee_type() {
600 let cursor_call_conv = ty.call_conv();
601 if cursor_call_conv != CXCallingConv_Invalid {
602 call_conv = cursor_call_conv;
603 }
604 }
605
606 let abi = get_abi(call_conv);
607
608 if abi.is_unknown() {
609 warn!("Unknown calling convention: {call_conv:?}");
610 }
611
612 Ok(Self {
613 name: spelling,
614 return_type: ret,
615 argument_types: args,
616 is_variadic: ty.is_variadic(),
617 is_divergent,
618 must_use,
619 abi,
620 })
621 }
622
623 pub(crate) fn return_type(&self) -> TypeId {
625 self.return_type
626 }
627
628 pub(crate) fn argument_types(&self) -> &[(Option<String>, TypeId)] {
630 &self.argument_types
631 }
632
633 pub(crate) fn abi(
635 &self,
636 ctx: &BindgenContext,
637 name: Option<&str>,
638 ) -> crate::codegen::error::Result<ClangAbi> {
639 let abi = if let Some(name) = name {
642 if let Some((abi, _)) = ctx
643 .options()
644 .abi_overrides
645 .iter()
646 .find(|(_, regex_set)| regex_set.matches(name))
647 {
648 ClangAbi::Known(*abi)
649 } else {
650 self.abi
651 }
652 } else if let Some((abi, _)) = ctx
653 .options()
654 .abi_overrides
655 .iter()
656 .find(|(_, regex_set)| regex_set.matches(&self.name))
657 {
658 ClangAbi::Known(*abi)
659 } else {
660 self.abi
661 };
662
663 match abi {
664 ClangAbi::Known(Abi::ThisCall)
665 if !ctx.options().rust_features().thiscall_abi =>
666 {
667 Err(crate::codegen::error::Error::UnsupportedAbi("thiscall"))
668 }
669 ClangAbi::Known(Abi::Vectorcall)
670 if !ctx.options().rust_features().vectorcall_abi =>
671 {
672 Err(crate::codegen::error::Error::UnsupportedAbi("vectorcall"))
673 }
674 ClangAbi::Known(Abi::CUnwind)
675 if !ctx.options().rust_features().c_unwind_abi =>
676 {
677 Err(crate::codegen::error::Error::UnsupportedAbi("C-unwind"))
678 }
679 ClangAbi::Known(Abi::EfiApi)
680 if !ctx.options().rust_features().abi_efiapi =>
681 {
682 Err(crate::codegen::error::Error::UnsupportedAbi("efiapi"))
683 }
684 ClangAbi::Known(Abi::Win64) if self.is_variadic() => {
685 Err(crate::codegen::error::Error::UnsupportedAbi("Win64"))
686 }
687 abi => Ok(abi),
688 }
689 }
690
691 pub(crate) fn is_variadic(&self) -> bool {
693 self.is_variadic && !self.argument_types.is_empty()
697 }
698
699 pub(crate) fn must_use(&self) -> bool {
701 self.must_use
702 }
703
704 pub(crate) fn function_pointers_can_derive(&self) -> bool {
714 if self.argument_types.len() > RUST_DERIVE_FUNPTR_LIMIT {
715 return false;
716 }
717
718 matches!(self.abi, ClangAbi::Known(Abi::C) | ClangAbi::Unknown(..))
719 }
720
721 pub(crate) fn is_divergent(&self) -> bool {
723 self.is_divergent
724 }
725}
726
727impl ClangSubItemParser for Function {
728 fn parse(
729 cursor: clang::Cursor,
730 context: &mut BindgenContext,
731 ) -> Result<ParseResult<Self>, ParseError> {
732 use ext_php_rs_clang_sys::*;
733
734 let Some(kind) = FunctionKind::from_cursor(&cursor) else {
735 return Err(ParseError::Continue);
736 };
737
738 debug!("Function::parse({cursor:?}, {:?})", cursor.cur_type());
739 let visibility = cursor.visibility();
740 if visibility != CXVisibility_Default {
741 return Err(ParseError::Continue);
742 }
743 if cursor.access_specifier() == CX_CXXPrivate
744 && !context.options().generate_private_functions
745 {
746 return Err(ParseError::Continue);
747 }
748
749 let linkage = cursor.linkage();
750 let linkage = match linkage {
751 CXLinkage_External | CXLinkage_UniqueExternal => Linkage::External,
752 CXLinkage_Internal => Linkage::Internal,
753 _ => return Err(ParseError::Continue),
754 };
755
756 if cursor.is_inlined_function()
757 || cursor.definition().is_some_and(|x| x.is_inlined_function())
758 {
759 if !context.options().generate_inline_functions
760 && !context.options().wrap_static_fns
761 {
762 return Err(ParseError::Continue);
763 }
764
765 if cursor.is_deleted_function()
766 && !context.options().generate_deleted_functions
767 {
768 return Err(ParseError::Continue);
769 }
770
771 if context.options().wrap_static_fns
773 && cursor.is_inlined_function()
774 && matches!(linkage, Linkage::External)
775 {
776 return Err(ParseError::Continue);
777 }
778 }
779
780 let sig = Item::from_ty(&cursor.cur_type(), cursor, None, context)?;
782
783 let mut name = cursor.spelling();
784 assert!(!name.is_empty(), "Empty function name?");
785
786 if cursor.kind() == CXCursor_Destructor {
787 if name.starts_with('~') {
791 name.remove(0);
792 }
793
794 name.push_str("_destructor");
798 }
799 if let Some(nm) = context.options().last_callback(|callbacks| {
800 callbacks.generated_name_override(ItemInfo {
801 name: name.as_str(),
802 kind: ItemKind::Function,
803 })
804 }) {
805 name = nm;
806 }
807 assert!(!name.is_empty(), "Empty function name.");
808
809 let mangled_name = cursor_mangling(context, &cursor);
810
811 let link_name = context.options().last_callback(|callbacks| {
812 callbacks.generated_link_name_override(ItemInfo {
813 name: name.as_str(),
814 kind: ItemKind::Function,
815 })
816 });
817
818 let function = Self::new(
819 name.clone(),
820 mangled_name,
821 link_name,
822 sig,
823 kind,
824 linkage,
825 );
826
827 Ok(ParseResult::New(function, Some(cursor)))
828 }
829}
830
831impl Trace for FunctionSig {
832 type Extra = ();
833
834 fn trace<T>(&self, _: &BindgenContext, tracer: &mut T, _: &())
835 where
836 T: Tracer,
837 {
838 tracer.visit_kind(self.return_type().into(), EdgeKind::FunctionReturn);
839
840 for &(_, ty) in self.argument_types() {
841 tracer.visit_kind(ty.into(), EdgeKind::FunctionParameter);
842 }
843 }
844}