Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,15 @@ All function attributes are just local overrides for the same options found in t
* prefix
* postfix

## Generating Swift Bindings

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.

This attribute is commonly used in Objective-C/C/C++ via the `NS_SWIFT_NAME` and `CF_SWIFT_NAME` macros.

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).

This is controlled by the `swift_name_macro` option in the cbindgen.toml.


## cbindgen.toml
Expand Down Expand Up @@ -545,6 +553,13 @@ args = "horizontal"
# default: nothing is emitted for must_use functions
must_use = "MUST_USE_FUNC"

# An optional string that, if present, will be used to generate Swift function
# and method signatures for generated functions, for example "CF_SWIFT_NAME".
# If no such macro is available in your toolchain, you can define one using the
# `header` option in cbindgen.toml
# default: no swift_name function attributes are generated
swift_name_macro = "CF_SWIFT_NAME"

# A rule to use to rename function argument names. The renaming assumes the input
# is the Rust standard snake_case, however it accepts all the different rename_args
# inputs. This means many options here are no-ops or redundant.
Expand Down
5 changes: 4 additions & 1 deletion src/bindgen/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,12 +279,14 @@ pub struct FunctionConfig {
pub prefix: Option<String>,
/// Optional text to output after each function declaration
pub postfix: Option<String>,
/// The way to annotation this function as #[must_use].
/// The way to annotation this function as #[must_use]
pub must_use: Option<String>,
/// The style to layout the args
pub args: Layout,
/// The rename rule to apply to function args
pub rename_args: Option<RenameRule>,
/// An optional macro to use when generating Swift function name attributes
pub swift_name_macro: Option<String>,
}

impl Default for FunctionConfig {
Expand All @@ -295,6 +297,7 @@ impl Default for FunctionConfig {
must_use: None,
args: Layout::Auto,
rename_args: None,
swift_name_macro: None,
}
}
}
Expand Down
103 changes: 96 additions & 7 deletions src/bindgen/ir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ use crate::bindgen::writer::{Source, SourceWriter};
#[derive(Debug, Clone)]
pub struct Function {
pub path: Path,
/// Path to the self-type of the function
/// If the function is a method, this will contain the path of the type in the impl block
pub self_type_path: Option<Path>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment saying something like "If we're a method, this will be Some, and the path will be the path of the type in the impl block" or such.

It'd be nice to make something more similar to what associated constants do, but it may be overkill for this.

pub ret: Type,
pub args: Vec<(String, Type)>,
pub extern_decl: bool,
Expand All @@ -34,12 +37,16 @@ pub struct Function {
impl Function {
pub fn load(
path: Path,
self_type_path: Option<Path>,
sig: &syn::Signature,
extern_decl: bool,
attrs: &[syn::Attribute],
mod_cfg: Option<&Cfg>,
) -> Result<Function, String> {
let args = sig.inputs.iter().try_skip_map(|x| x.as_ident_and_type())?;
let args = sig
.inputs
.iter()
.try_skip_map(|x| x.as_ident_and_type(self_type_path.as_ref()))?;
let ret = match sig.output {
syn::ReturnType::Default => Type::Primitive(PrimitiveType::Void),
syn::ReturnType::Type(_, ref ty) => {
Expand All @@ -53,6 +60,7 @@ impl Function {

Ok(Function {
path,
self_type_path,
ret,
args,
extern_decl,
Expand All @@ -62,6 +70,35 @@ impl Function {
})
}

pub fn swift_name(&self) -> String {
// If the symbol name starts with the type name, separate the two components with '.'
// so that Swift recognises the association between the method and the type
let (ref type_prefix, ref type_name) = if let Some(type_name) = &self.self_type_path {
let type_name = type_name.to_string();
if !self.path.name().starts_with(&type_name) {
return self.path.to_string();
}
(format!("{}.", type_name), type_name)
} else {
("".to_string(), "".to_string())
};

let item_name = self
.path
.name()
.trim_start_matches(type_name)
.trim_start_matches('_');

let item_args = {
let mut items = vec![];
for (arg, _) in self.args.iter() {
items.push(format!("{}:", arg.as_str()));
}
items.join("")
};
format!("{}{}({})", type_prefix, item_name, item_args)
}

pub fn path(&self) -> &Path {
&self.path
}
Expand Down Expand Up @@ -160,12 +197,17 @@ impl Source for Function {
}
}
cdecl::write_func(out, &func, false, void_prototype);

if !func.extern_decl {
if let Some(ref postfix) = postfix {
out.write(" ");
write!(out, "{}", postfix);
write!(out, " {}", postfix);
}
}

if let Some(ref swift_name_macro) = config.function.swift_name_macro {
write!(out, " {}({})", swift_name_macro, func.swift_name());
}

out.write(";");

condition.write_after(config, out);
Expand Down Expand Up @@ -203,6 +245,11 @@ impl Source for Function {
write!(out, "{}", postfix);
}
}

if let Some(ref swift_name_macro) = config.function.swift_name_macro {
write!(out, " {}({})", swift_name_macro, func.swift_name());
}

out.write(";");

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

pub trait SynFnArgHelpers {
fn as_ident_and_type(&self) -> Result<Option<(String, Type)>, String>;
fn as_ident_and_type(
&self,
self_type_path: Option<&Path>,
) -> Result<Option<(String, Type)>, String>;
}

fn gen_self_type(self_type_path: &Path, receiver: &syn::Receiver) -> Result<Option<Type>, String> {
fn _gen_self_type(
self_type_path: &Path,
receiver: &syn::Receiver,
) -> Result<syn::Type, syn::Error> {
Ok(match receiver.reference {
Some(_) => syn::Type::Reference(match receiver.mutability {
Some(_) => syn::parse_str(&format!("&mut {}", self_type_path))?,
None => syn::parse_str(&format!("&{}", self_type_path))?,
}),
None => syn::Type::Path(syn::parse_str(self_type_path.name())?),
})
}

Type::load(&Box::new(
_gen_self_type(self_type_path, receiver)
.map_err(|e| format!("Failed to generate self type: {}", e))?,
))
}

impl SynFnArgHelpers for syn::FnArg {
fn as_ident_and_type(&self) -> Result<Option<(String, Type)>, String> {
fn as_ident_and_type(
&self,
self_type_path: Option<&Path>,
) -> Result<Option<(String, Type)>, String> {
match self {
&syn::FnArg::Typed(syn::PatType {
ref pat, ref ty, ..
}) => match **pat {
syn::Pat::Ident(syn::PatIdent { ref ident, .. }) => {
if let Some(x) = Type::load(ty)? {
if let Some(mut x) = Type::load(ty)? {
if let Some(self_type_path) = self_type_path {
x.replace_self_with(self_type_path)
}
Ok(Some((ident.to_string(), x)))
} else {
Ok(None)
}
}
_ => Err("Parameter has an unsupported type.".to_owned()),
},
_ => Err("Parameter has an unsupported type.".to_owned()),
&syn::FnArg::Receiver(ref receiver) => {
if let Some(self_type_path) = self_type_path {
if let Some(x) = gen_self_type(self_type_path, receiver)? {
Ok(Some(("self".to_string(), x)))
} else {
Ok(None)
}
} else {
Err(
"Parameter has an unsupported type (Self type found in free function)"
.to_owned(),
)
}
}
}
}
}
Loading