Skip to content

Commit f2e3016

Browse files
committed
Support 'swift_name' attributes on generated functions
Fixes #420
1 parent 3639d6b commit f2e3016

File tree

13 files changed

+440
-39
lines changed

13 files changed

+440
-39
lines changed

src/bindgen/config.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,8 @@ pub struct FunctionConfig {
285285
pub args: Layout,
286286
/// The rename rule to apply to function args
287287
pub rename_args: Option<RenameRule>,
288+
/// Whether to guess an appropriate swift_name attribute
289+
pub use_swift_name: bool,
288290
}
289291

290292
impl Default for FunctionConfig {
@@ -295,6 +297,7 @@ impl Default for FunctionConfig {
295297
must_use: None,
296298
args: Layout::Auto,
297299
rename_args: None,
300+
use_swift_name: false,
298301
}
299302
}
300303
}

src/bindgen/ir/function.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ pub struct Function {
2929
pub cfg: Option<Cfg>,
3030
pub annotations: AnnotationSet,
3131
pub documentation: Documentation,
32+
pub attributes: Vec<String>,
3233
}
3334

3435
impl Function {
@@ -59,6 +60,7 @@ impl Function {
5960
cfg: Cfg::append(mod_cfg, Cfg::load(attrs)),
6061
annotations: AnnotationSet::load(attrs)?,
6162
documentation: Documentation::load(attrs),
63+
attributes: Vec::new(),
6264
})
6365
}
6466

@@ -166,6 +168,15 @@ impl Source for Function {
166168
write!(out, "{}", postfix);
167169
}
168170
}
171+
172+
let attributes = func
173+
.attributes
174+
.iter()
175+
.map(|s| format!(" __attribute__(({}))", s))
176+
.collect::<Vec<_>>()
177+
.join("");
178+
write!(out, "{}", attributes);
179+
169180
out.write(";");
170181

171182
condition.write_after(config, out);
@@ -203,6 +214,12 @@ impl Source for Function {
203214
write!(out, "{}", postfix);
204215
}
205216
}
217+
218+
for attr in func.attributes.iter() {
219+
out.new_line();
220+
write!(out, "__attribute__(({}))", attr);
221+
}
222+
206223
out.write(";");
207224

208225
condition.write_after(config, out);

src/bindgen/parser.rs

Lines changed: 98 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,24 @@ impl Parse {
465465
if has_assoc_const {
466466
impls_with_assoc_consts.push(item_impl);
467467
}
468+
469+
if let syn::Type::Path(ref path) = *item_impl.self_ty {
470+
if let Some(type_name) = path.path.get_ident() {
471+
for method in item_impl.items.iter().filter_map(|item| match item {
472+
syn::ImplItem::Method(method) => Some(method),
473+
_ => None,
474+
}) {
475+
self.load_syn_method(
476+
config,
477+
binding_crate_name,
478+
crate_name,
479+
mod_cfg,
480+
type_name,
481+
method,
482+
)
483+
}
484+
}
485+
}
468486
}
469487
syn::Item::Macro(ref item) => {
470488
self.load_builtin_macro(config, crate_name, mod_cfg, item)
@@ -540,6 +558,36 @@ impl Parse {
540558
}
541559
}
542560

561+
/// Loads a `fn` declaration inside an `impl` block, if the type is a simple identifier
562+
fn load_syn_method(
563+
&mut self,
564+
config: &Config,
565+
binding_crate_name: &str,
566+
crate_name: &str,
567+
mod_cfg: Option<&Cfg>,
568+
type_name: &syn::Ident,
569+
item: &syn::ImplItemMethod,
570+
) {
571+
if let Some(mut func) = self.load_fn_declaration(
572+
config,
573+
binding_crate_name,
574+
crate_name,
575+
mod_cfg,
576+
item,
577+
&item.sig,
578+
&item.vis,
579+
&item.attrs,
580+
) {
581+
if config.function.use_swift_name {
582+
func.attributes.push(format!(
583+
"swift_name(\"{}\")",
584+
item.swift_name(Some(type_name))
585+
))
586+
}
587+
self.functions.push(func);
588+
}
589+
}
590+
543591
/// Loads a `fn` declaration
544592
fn load_syn_fn(
545593
&mut self,
@@ -549,65 +597,76 @@ impl Parse {
549597
mod_cfg: Option<&Cfg>,
550598
item: &syn::ItemFn,
551599
) {
600+
if let Some(mut func) = self.load_fn_declaration(
601+
config,
602+
binding_crate_name,
603+
crate_name,
604+
mod_cfg,
605+
item,
606+
&item.sig,
607+
&item.vis,
608+
&item.attrs,
609+
) {
610+
if config.function.use_swift_name {
611+
func.attributes
612+
.push(format!("swift_name(\"{}\")", item.swift_name(None)))
613+
}
614+
self.functions.push(func);
615+
}
616+
}
617+
618+
fn load_fn_declaration(
619+
&mut self,
620+
config: &Config,
621+
binding_crate_name: &str,
622+
crate_name: &str,
623+
mod_cfg: Option<&Cfg>,
624+
named_symbol: &dyn SynItemFnHelpers,
625+
sig: &syn::Signature,
626+
vis: &syn::Visibility,
627+
attrs: &[syn::Attribute],
628+
) -> Option<Function> {
552629
if !config
553630
.parse
554631
.should_generate_top_level_item(crate_name, binding_crate_name)
555632
{
556633
info!(
557634
"Skip {}::{} - (fn's outside of the binding crate are not used).",
558-
crate_name, &item.sig.ident
635+
crate_name, &sig.ident
559636
);
560-
return;
637+
return None;
561638
}
562639

563-
if let syn::Visibility::Public(_) = item.vis {
564-
if item.sig.abi.is_omitted() || item.sig.abi.is_c() {
565-
if let Some(exported_name) = item.exported_name() {
640+
if let syn::Visibility::Public(_) = vis {
641+
if sig.abi.is_omitted() || sig.abi.is_c() {
642+
if let Some(exported_name) = named_symbol.exported_name() {
566643
let path = Path::new(exported_name);
567-
match Function::load(path, &item.sig, false, &item.attrs, mod_cfg) {
644+
match Function::load(path, &sig, false, &attrs, mod_cfg) {
568645
Ok(func) => {
569-
info!("Take {}::{}.", crate_name, &item.sig.ident);
570-
571-
self.functions.push(func);
646+
info!("Take {}::{}.", crate_name, &sig.ident);
647+
return Some(func);
572648
}
573649
Err(msg) => {
574-
error!(
575-
"Cannot use fn {}::{} ({}).",
576-
crate_name, &item.sig.ident, msg
577-
);
650+
error!("Cannot use fn {}::{} ({}).", crate_name, &sig.ident, msg);
578651
}
579652
}
580-
return;
653+
} else {
654+
warn!(
655+
"Skipping {}::{} - (not `no_mangle`, and has no `export_name` attribute)",
656+
crate_name, &sig.ident
657+
);
581658
}
659+
} else {
660+
warn!(
661+
"Skipping {}::{} - (not `extern \"C\"`",
662+
crate_name, &sig.ident
663+
);
582664
}
583-
}
584-
585-
// TODO
586-
if let syn::Visibility::Public(_) = item.vis {
587665
} else {
588-
warn!("Skip {}::{} - (not `pub`).", crate_name, &item.sig.ident);
589-
}
590-
591-
if !(item.sig.abi.is_omitted() || item.sig.abi.is_c()) {
592-
warn!(
593-
"Skip {}::{} - (wrong ABI - not `extern` or `extern \"C\"`).",
594-
crate_name, &item.sig.ident
595-
);
596-
}
597-
598-
if item.exported_name().is_none() {
599-
warn!(
600-
"Skip {}::{} - (not `no_mangle`, and has no `export_name` attribute)",
601-
crate_name, &item.sig.ident
602-
);
666+
warn!("Skipping {}::{} - (not `pub`)", crate_name, &sig.ident);
603667
}
604668

605-
if item.sig.abi.is_some() && !(item.sig.abi.is_omitted() || item.sig.abi.is_c()) {
606-
warn!(
607-
"Skip {}::{} - (non `extern \"C\"`).",
608-
crate_name, &item.sig.ident
609-
);
610-
}
669+
None
611670
}
612671

613672
/// Loads associated `const` declarations

src/bindgen/utilities.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,53 @@ pub fn find_first_some<T>(slice: &[Option<T>]) -> Option<&T> {
4141

4242
pub trait SynItemFnHelpers: SynItemHelpers {
4343
fn exported_name(&self) -> Option<String>;
44+
45+
fn swift_name(&self, type_name: Option<&syn::Ident>) -> String;
46+
}
47+
48+
fn gen_swift_name(
49+
type_name: Option<&syn::Ident>,
50+
symbol_name: &str,
51+
arguments: &mut dyn Iterator<Item = &syn::FnArg>,
52+
) -> String {
53+
use crate::bindgen::ir::Path;
54+
55+
let path = Path::new(symbol_name);
56+
57+
// If the symbol name starts with the type name, separate the two components with '.'
58+
// so that Swift recognises the association between the method and the type
59+
let (type_prefix, type_name) = if let Some(type_name) = type_name {
60+
let type_name = type_name.to_string();
61+
if !path.name().starts_with(&type_name) {
62+
return symbol_name.to_string();
63+
}
64+
(format!("{}.", type_name), type_name)
65+
} else {
66+
("".to_string(), "".to_string())
67+
};
68+
69+
let item_name = path
70+
.name()
71+
.trim_start_matches(&type_name)
72+
.trim_start_matches('_');
73+
74+
let item_args = {
75+
let mut items = vec![];
76+
for arg in arguments {
77+
match arg {
78+
syn::FnArg::Receiver(_) => {
79+
items.push("self:".to_string());
80+
}
81+
syn::FnArg::Typed(ref pat_type) => {
82+
if let syn::Pat::Ident(ref ident) = *pat_type.pat {
83+
items.push(format!("{}:", ident.ident));
84+
}
85+
}
86+
}
87+
}
88+
items.join("")
89+
};
90+
format!("{}{}({})", type_prefix, item_name, item_args)
4491
}
4592

4693
impl SynItemFnHelpers for syn::ItemFn {
@@ -55,6 +102,36 @@ impl SynItemFnHelpers for syn::ItemFn {
55102
}
56103
})
57104
}
105+
106+
fn swift_name(&self, type_name: Option<&syn::Ident>) -> String {
107+
gen_swift_name(
108+
type_name,
109+
&self.exported_name().unwrap(),
110+
&mut self.sig.inputs.iter(),
111+
)
112+
}
113+
}
114+
115+
impl SynItemFnHelpers for syn::ImplItemMethod {
116+
fn exported_name(&self) -> Option<String> {
117+
self.attrs
118+
.attr_name_value_lookup("export_name")
119+
.or_else(|| {
120+
if self.is_no_mangle() {
121+
Some(self.sig.ident.to_string())
122+
} else {
123+
None
124+
}
125+
})
126+
}
127+
128+
fn swift_name(&self, type_name: Option<&syn::Ident>) -> String {
129+
gen_swift_name(
130+
type_name,
131+
&self.exported_name().unwrap(),
132+
&mut self.sig.inputs.iter(),
133+
)
134+
}
58135
}
59136

60137
pub trait SynItemHelpers {
@@ -138,6 +215,7 @@ impl_syn_item_helper!(syn::ItemUse);
138215
impl_syn_item_helper!(syn::ItemStatic);
139216
impl_syn_item_helper!(syn::ItemConst);
140217
impl_syn_item_helper!(syn::ItemFn);
218+
impl_syn_item_helper!(syn::ImplItemMethod);
141219
impl_syn_item_helper!(syn::ItemMod);
142220
impl_syn_item_helper!(syn::ItemForeignMod);
143221
impl_syn_item_helper!(syn::ItemType);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#include <stdarg.h>
2+
#include <stdbool.h>
3+
#include <stdint.h>
4+
#include <stdlib.h>
5+
6+
typedef struct Foo Foo;
7+
8+
typedef struct FooRef {
9+
Foo *ptr;
10+
} FooRef;
11+
12+
FooRef FooRef_create(void) __attribute__((swift_name("FooRef.create()")));
13+
14+
int32_t FooRef_doThing(FooRef self)
15+
/*a comment!*/
16+
__attribute__((swift_name("FooRef.doThing(self:)")));
17+
18+
int32_t FooRef_getBar(FooRef self) __attribute__((swift_name("FooRef.getBar(self:)")));
19+
20+
void FooRef_setBar(FooRef self,
21+
int32_t bar)
22+
__attribute__((swift_name("FooRef.setBar(self:bar:)")));
23+
24+
void do_the_thing(void) __attribute__((swift_name("do_the_thing()")));
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#include <stdarg.h>
2+
#include <stdbool.h>
3+
#include <stdint.h>
4+
#include <stdlib.h>
5+
6+
typedef struct Foo Foo;
7+
8+
typedef struct FooRef {
9+
Foo *ptr;
10+
} FooRef;
11+
12+
#ifdef __cplusplus
13+
extern "C" {
14+
#endif // __cplusplus
15+
16+
FooRef FooRef_create(void) __attribute__((swift_name("FooRef.create()")));
17+
18+
int32_t FooRef_doThing(FooRef self)
19+
/*a comment!*/
20+
__attribute__((swift_name("FooRef.doThing(self:)")));
21+
22+
int32_t FooRef_getBar(FooRef self) __attribute__((swift_name("FooRef.getBar(self:)")));
23+
24+
void FooRef_setBar(FooRef self,
25+
int32_t bar)
26+
__attribute__((swift_name("FooRef.setBar(self:bar:)")));
27+
28+
void do_the_thing(void) __attribute__((swift_name("do_the_thing()")));
29+
30+
#ifdef __cplusplus
31+
} // extern "C"
32+
#endif // __cplusplus

0 commit comments

Comments
 (0)