Skip to content

Commit cf406e9

Browse files
alaroldaiemilio
authored andcommitted
Support 'swift_name' attributes on generated functions
Fixes #420
1 parent ac1a7d4 commit cf406e9

File tree

14 files changed

+749
-47
lines changed

14 files changed

+749
-47
lines changed

docs.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,15 @@ All function attributes are just local overrides for the same options found in t
293293
* prefix
294294
* postfix
295295

296+
## Generating Swift Bindings
296297

298+
In addition to parsing function names in C/C++ header files, the Swift compiler can make use of the `swift_name` attribute on functions to generate more idiomatic names for imported functions and methods.
299+
300+
This attribute is commonly used in Objective-C/C/C++ via the `NS_SWIFT_NAME` and `CF_SWIFT_NAME` macros.
301+
302+
Given configuration in the cbindgen.toml, `cbindgen` can generate these attributes for you by guessing an appropriate method signature based on the existing function name (and type, if it is a method in an `impl` block).
303+
304+
This is controlled by the `swift_name_macro` option in the cbindgen.toml.
297305

298306

299307
## cbindgen.toml
@@ -545,6 +553,13 @@ args = "horizontal"
545553
# default: nothing is emitted for must_use functions
546554
must_use = "MUST_USE_FUNC"
547555

556+
# An optional string that, if present, will be used to generate Swift function
557+
# and method signatures for generated functions, for example "CF_SWIFT_NAME".
558+
# If no such macro is available in your toolchain, you can define one using the
559+
# `header` option in cbindgen.toml
560+
# default: no swift_name function attributes are generated
561+
swift_name_macro = "CF_SWIFT_NAME"
562+
548563
# A rule to use to rename function argument names. The renaming assumes the input
549564
# is the Rust standard snake_case, however it accepts all the different rename_args
550565
# inputs. This means many options here are no-ops or redundant.

src/bindgen/config.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,12 +279,14 @@ pub struct FunctionConfig {
279279
pub prefix: Option<String>,
280280
/// Optional text to output after each function declaration
281281
pub postfix: Option<String>,
282-
/// The way to annotation this function as #[must_use].
282+
/// The way to annotation this function as #[must_use]
283283
pub must_use: Option<String>,
284284
/// The style to layout the args
285285
pub args: Layout,
286286
/// The rename rule to apply to function args
287287
pub rename_args: Option<RenameRule>,
288+
/// An optional macro to use when generating Swift function name attributes
289+
pub swift_name_macro: Option<String>,
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+
swift_name_macro: None,
298301
}
299302
}
300303
}

src/bindgen/ir/function.rs

Lines changed: 96 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ use crate::bindgen::writer::{Source, SourceWriter};
2323
#[derive(Debug, Clone)]
2424
pub struct Function {
2525
pub path: Path,
26+
/// Path to the self-type of the function
27+
/// If the function is a method, this will contain the path of the type in the impl block
28+
pub self_type_path: Option<Path>,
2629
pub ret: Type,
2730
pub args: Vec<(String, Type)>,
2831
pub extern_decl: bool,
@@ -34,12 +37,16 @@ pub struct Function {
3437
impl Function {
3538
pub fn load(
3639
path: Path,
40+
self_type_path: Option<Path>,
3741
sig: &syn::Signature,
3842
extern_decl: bool,
3943
attrs: &[syn::Attribute],
4044
mod_cfg: Option<&Cfg>,
4145
) -> Result<Function, String> {
42-
let args = sig.inputs.iter().try_skip_map(|x| x.as_ident_and_type())?;
46+
let args = sig
47+
.inputs
48+
.iter()
49+
.try_skip_map(|x| x.as_ident_and_type(self_type_path.as_ref()))?;
4350
let ret = match sig.output {
4451
syn::ReturnType::Default => Type::Primitive(PrimitiveType::Void),
4552
syn::ReturnType::Type(_, ref ty) => {
@@ -53,6 +60,7 @@ impl Function {
5360

5461
Ok(Function {
5562
path,
63+
self_type_path,
5664
ret,
5765
args,
5866
extern_decl,
@@ -62,6 +70,35 @@ impl Function {
6270
})
6371
}
6472

73+
pub fn swift_name(&self) -> String {
74+
// If the symbol name starts with the type name, separate the two components with '.'
75+
// so that Swift recognises the association between the method and the type
76+
let (ref type_prefix, ref type_name) = if let Some(type_name) = &self.self_type_path {
77+
let type_name = type_name.to_string();
78+
if !self.path.name().starts_with(&type_name) {
79+
return self.path.to_string();
80+
}
81+
(format!("{}.", type_name), type_name)
82+
} else {
83+
("".to_string(), "".to_string())
84+
};
85+
86+
let item_name = self
87+
.path
88+
.name()
89+
.trim_start_matches(type_name)
90+
.trim_start_matches('_');
91+
92+
let item_args = {
93+
let mut items = vec![];
94+
for (arg, _) in self.args.iter() {
95+
items.push(format!("{}:", arg.as_str()));
96+
}
97+
items.join("")
98+
};
99+
format!("{}{}({})", type_prefix, item_name, item_args)
100+
}
101+
65102
pub fn path(&self) -> &Path {
66103
&self.path
67104
}
@@ -160,12 +197,17 @@ impl Source for Function {
160197
}
161198
}
162199
cdecl::write_func(out, &func, false, void_prototype);
200+
163201
if !func.extern_decl {
164202
if let Some(ref postfix) = postfix {
165-
out.write(" ");
166-
write!(out, "{}", postfix);
203+
write!(out, " {}", postfix);
167204
}
168205
}
206+
207+
if let Some(ref swift_name_macro) = config.function.swift_name_macro {
208+
write!(out, " {}({})", swift_name_macro, func.swift_name());
209+
}
210+
169211
out.write(";");
170212

171213
condition.write_after(config, out);
@@ -203,6 +245,11 @@ impl Source for Function {
203245
write!(out, "{}", postfix);
204246
}
205247
}
248+
249+
if let Some(ref swift_name_macro) = config.function.swift_name_macro {
250+
write!(out, " {}({})", swift_name_macro, func.swift_name());
251+
}
252+
206253
out.write(";");
207254

208255
condition.write_after(config, out);
@@ -221,25 +268,67 @@ impl Source for Function {
221268
}
222269

223270
pub trait SynFnArgHelpers {
224-
fn as_ident_and_type(&self) -> Result<Option<(String, Type)>, String>;
271+
fn as_ident_and_type(
272+
&self,
273+
self_type_path: Option<&Path>,
274+
) -> Result<Option<(String, Type)>, String>;
275+
}
276+
277+
fn gen_self_type(self_type_path: &Path, receiver: &syn::Receiver) -> Result<Option<Type>, String> {
278+
fn _gen_self_type(
279+
self_type_path: &Path,
280+
receiver: &syn::Receiver,
281+
) -> Result<syn::Type, syn::Error> {
282+
Ok(match receiver.reference {
283+
Some(_) => syn::Type::Reference(match receiver.mutability {
284+
Some(_) => syn::parse_str(&format!("&mut {}", self_type_path))?,
285+
None => syn::parse_str(&format!("&{}", self_type_path))?,
286+
}),
287+
None => syn::Type::Path(syn::parse_str(self_type_path.name())?),
288+
})
289+
}
290+
291+
Type::load(&Box::new(
292+
_gen_self_type(self_type_path, receiver)
293+
.map_err(|e| format!("Failed to generate self type: {}", e))?,
294+
))
225295
}
226296

227297
impl SynFnArgHelpers for syn::FnArg {
228-
fn as_ident_and_type(&self) -> Result<Option<(String, Type)>, String> {
298+
fn as_ident_and_type(
299+
&self,
300+
self_type_path: Option<&Path>,
301+
) -> Result<Option<(String, Type)>, String> {
229302
match self {
230303
&syn::FnArg::Typed(syn::PatType {
231304
ref pat, ref ty, ..
232305
}) => match **pat {
233306
syn::Pat::Ident(syn::PatIdent { ref ident, .. }) => {
234-
if let Some(x) = Type::load(ty)? {
307+
if let Some(mut x) = Type::load(ty)? {
308+
if let Some(self_type_path) = self_type_path {
309+
x.replace_self_with(self_type_path)
310+
}
235311
Ok(Some((ident.to_string(), x)))
236312
} else {
237313
Ok(None)
238314
}
239315
}
240316
_ => Err("Parameter has an unsupported type.".to_owned()),
241317
},
242-
_ => Err("Parameter has an unsupported type.".to_owned()),
318+
&syn::FnArg::Receiver(ref receiver) => {
319+
if let Some(self_type_path) = self_type_path {
320+
if let Some(x) = gen_self_type(self_type_path, receiver)? {
321+
Ok(Some(("self".to_string(), x)))
322+
} else {
323+
Ok(None)
324+
}
325+
} else {
326+
Err(
327+
"Parameter has an unsupported type (Self type found in free function)"
328+
.to_owned(),
329+
)
330+
}
331+
}
243332
}
244333
}
245334
}

0 commit comments

Comments
 (0)