Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned};
use syn::{visit_mut::VisitMut, Item, Type};
mod attributes;
mod callback_interface;
mod item;
mod scaffolding;
mod trait_interface;
mod utrait;
use self::{
item::{ExportItem, ImplItem},
scaffolding::{
gen_constructor_scaffolding, gen_ffi_function, gen_fn_scaffolding, gen_method_scaffolding,
},
};
use crate::util::{ident_to_string, mod_path};
pub use attributes::{AsyncRuntime, DefaultMap, ExportFnArgs};
pub use callback_interface::ffi_converter_callback_interface_impl;
// TODO(jplatte): Ensure no generics, …
// TODO(jplatte): Aggregate errors instead of short-circuiting, wherever possible
pub(crate) fn expand_export(
mut item: Item,
all_args: proc_macro::TokenStream,
udl_mode: bool,
) -> syn::Result<TokenStream> {
let mod_path = mod_path()?;
// If the input is an `impl` block, rewrite any uses of the `Self` type
// alias to the actual type, so we don't have to special-case it in the
// metadata collection or scaffolding code generation (which generates
// new functions outside of the `impl`).
rewrite_self_type(&mut item);
let metadata = ExportItem::new(item, all_args)?;
match metadata {
ExportItem::Function { sig, args } => {
gen_fn_scaffolding(sig, args.async_runtime.as_ref(), udl_mode)
}
ExportItem::Impl {
items,
self_ident,
args,
} => {
if let Some(rt) = &args.async_runtime {
if items
.iter()
.all(|item| !matches!(item, ImplItem::Method(sig) if sig.is_async))
{
return Err(syn::Error::new_spanned(
rt,
"no async methods in this impl block",
));
}
}
let item_tokens: TokenStream = items
.into_iter()
.map(|item| match item {
ImplItem::Constructor(sig) => {
let async_runtime =
sig.async_runtime.clone().or(args.async_runtime.clone());
gen_constructor_scaffolding(sig, async_runtime.as_ref(), udl_mode)
}
ImplItem::Method(sig) => {
let async_runtime =
sig.async_runtime.clone().or(args.async_runtime.clone());
gen_method_scaffolding(sig, async_runtime.as_ref(), udl_mode)
}
})
.collect::<syn::Result<_>>()?;
Ok(quote_spanned! { self_ident.span() => #item_tokens })
}
ExportItem::Trait {
items,
self_ident,
with_foreign,
callback_interface_only: false,
docstring,
args,
} => trait_interface::gen_trait_scaffolding(
&mod_path,
args,
self_ident,
items,
udl_mode,
with_foreign,
docstring,
),
ExportItem::Trait {
items,
self_ident,
callback_interface_only: true,
docstring,
..
} => {
let trait_name = ident_to_string(&self_ident);
let trait_impl_ident = callback_interface::trait_impl_ident(&trait_name);
let trait_impl = callback_interface::trait_impl(&mod_path, &self_ident, &items)
.unwrap_or_else(|e| e.into_compile_error());
let metadata_items = (!udl_mode).then(|| {
let items =
callback_interface::metadata_items(&self_ident, &items, &mod_path, docstring)
.unwrap_or_else(|e| vec![e.into_compile_error()]);
quote! { #(#items)* }
});
let ffi_converter_tokens =
ffi_converter_callback_interface_impl(&self_ident, &trait_impl_ident, udl_mode);
Ok(quote! {
#trait_impl
#ffi_converter_tokens
#metadata_items
})
}
ExportItem::Struct {
self_ident,
uniffi_traits,
..
} => {
assert!(!udl_mode);
utrait::expand_uniffi_trait_export(self_ident, uniffi_traits)
}
}
}
/// Rewrite Self type alias usage in an impl block to the type itself.
///
/// For example,
///
/// ```ignore
/// impl some::module::Foo {
/// fn method(
/// self: Arc<Self>,
/// arg: Option<Bar<(), Self>>,
/// ) -> Result<Self, Error> {
/// todo!()
/// }
/// }
/// ```
///
/// will be rewritten to
///
/// ```ignore
/// impl some::module::Foo {
/// fn method(
/// self: Arc<some::module::Foo>,
/// arg: Option<Bar<(), some::module::Foo>>,
/// ) -> Result<some::module::Foo, Error> {
/// todo!()
/// }
/// }
/// ```
pub fn rewrite_self_type(item: &mut Item) {
let item = match item {
Item::Impl(i) => i,
_ => return,
};
struct RewriteSelfVisitor<'a>(&'a Type);
impl<'a> VisitMut for RewriteSelfVisitor<'a> {
fn visit_type_mut(&mut self, i: &mut Type) {
match i {
Type::Path(p) if p.qself.is_none() && p.path.is_ident("Self") => {
*i = self.0.clone();
}
_ => syn::visit_mut::visit_type_mut(self, i),
}
}
}
let mut visitor = RewriteSelfVisitor(&item.self_ty);
for item in &mut item.items {
visitor.visit_impl_item_mut(item);
}
}