1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 use proc_macro2::TokenStream;
6 use quote::{quote, quote_spanned};
7 use syn::{visit_mut::VisitMut, Item, Type};
8
9 mod attributes;
10 mod callback_interface;
11 mod item;
12 mod scaffolding;
13 mod trait_interface;
14 mod utrait;
15
16 use self::{
17 item::{ExportItem, ImplItem},
18 scaffolding::{
19 gen_constructor_scaffolding, gen_ffi_function, gen_fn_scaffolding, gen_method_scaffolding,
20 },
21 };
22 use crate::util::{ident_to_string, mod_path};
23 pub use attributes::{DefaultMap, ExportFnArgs, ExportedImplFnArgs};
24 pub use callback_interface::ffi_converter_callback_interface_impl;
25
26 // TODO(jplatte): Ensure no generics, …
27 // TODO(jplatte): Aggregate errors instead of short-circuiting, wherever possible
28
expand_export( mut item: Item, all_args: proc_macro::TokenStream, udl_mode: bool, ) -> syn::Result<TokenStream>29 pub(crate) fn expand_export(
30 mut item: Item,
31 all_args: proc_macro::TokenStream,
32 udl_mode: bool,
33 ) -> syn::Result<TokenStream> {
34 let mod_path = mod_path()?;
35 // If the input is an `impl` block, rewrite any uses of the `Self` type
36 // alias to the actual type, so we don't have to special-case it in the
37 // metadata collection or scaffolding code generation (which generates
38 // new functions outside of the `impl`).
39 rewrite_self_type(&mut item);
40
41 let metadata = ExportItem::new(item, all_args)?;
42
43 match metadata {
44 ExportItem::Function { sig, args } => {
45 gen_fn_scaffolding(sig, &args.async_runtime, udl_mode)
46 }
47 ExportItem::Impl {
48 items,
49 self_ident,
50 args,
51 } => {
52 if let Some(rt) = &args.async_runtime {
53 if items
54 .iter()
55 .all(|item| !matches!(item, ImplItem::Method(sig) if sig.is_async))
56 {
57 return Err(syn::Error::new_spanned(
58 rt,
59 "no async methods in this impl block",
60 ));
61 }
62 }
63
64 let item_tokens: TokenStream = items
65 .into_iter()
66 .map(|item| match item {
67 ImplItem::Constructor(sig) => {
68 gen_constructor_scaffolding(sig, &args.async_runtime, udl_mode)
69 }
70 ImplItem::Method(sig) => {
71 gen_method_scaffolding(sig, &args.async_runtime, udl_mode)
72 }
73 })
74 .collect::<syn::Result<_>>()?;
75 Ok(quote_spanned! { self_ident.span() => #item_tokens })
76 }
77 ExportItem::Trait {
78 items,
79 self_ident,
80 with_foreign,
81 callback_interface_only: false,
82 docstring,
83 args,
84 } => trait_interface::gen_trait_scaffolding(
85 &mod_path,
86 args,
87 self_ident,
88 items,
89 udl_mode,
90 with_foreign,
91 docstring,
92 ),
93 ExportItem::Trait {
94 items,
95 self_ident,
96 callback_interface_only: true,
97 docstring,
98 ..
99 } => {
100 let trait_name = ident_to_string(&self_ident);
101 let trait_impl_ident = callback_interface::trait_impl_ident(&trait_name);
102 let trait_impl = callback_interface::trait_impl(&mod_path, &self_ident, &items)
103 .unwrap_or_else(|e| e.into_compile_error());
104 let metadata_items = (!udl_mode).then(|| {
105 let items =
106 callback_interface::metadata_items(&self_ident, &items, &mod_path, docstring)
107 .unwrap_or_else(|e| vec![e.into_compile_error()]);
108 quote! { #(#items)* }
109 });
110 let ffi_converter_tokens =
111 ffi_converter_callback_interface_impl(&self_ident, &trait_impl_ident, udl_mode);
112
113 Ok(quote! {
114 #trait_impl
115
116 #ffi_converter_tokens
117
118 #metadata_items
119 })
120 }
121 ExportItem::Struct {
122 self_ident,
123 uniffi_traits,
124 ..
125 } => {
126 assert!(!udl_mode);
127 utrait::expand_uniffi_trait_export(self_ident, uniffi_traits)
128 }
129 }
130 }
131
132 /// Rewrite Self type alias usage in an impl block to the type itself.
133 ///
134 /// For example,
135 ///
136 /// ```ignore
137 /// impl some::module::Foo {
138 /// fn method(
139 /// self: Arc<Self>,
140 /// arg: Option<Bar<(), Self>>,
141 /// ) -> Result<Self, Error> {
142 /// todo!()
143 /// }
144 /// }
145 /// ```
146 ///
147 /// will be rewritten to
148 ///
149 /// ```ignore
150 /// impl some::module::Foo {
151 /// fn method(
152 /// self: Arc<some::module::Foo>,
153 /// arg: Option<Bar<(), some::module::Foo>>,
154 /// ) -> Result<some::module::Foo, Error> {
155 /// todo!()
156 /// }
157 /// }
158 /// ```
rewrite_self_type(item: &mut Item)159 pub fn rewrite_self_type(item: &mut Item) {
160 let item = match item {
161 Item::Impl(i) => i,
162 _ => return,
163 };
164
165 struct RewriteSelfVisitor<'a>(&'a Type);
166
167 impl<'a> VisitMut for RewriteSelfVisitor<'a> {
168 fn visit_type_mut(&mut self, i: &mut Type) {
169 match i {
170 Type::Path(p) if p.qself.is_none() && p.path.is_ident("Self") => {
171 *i = self.0.clone();
172 }
173 _ => syn::visit_mut::visit_type_mut(self, i),
174 }
175 }
176 }
177
178 let mut visitor = RewriteSelfVisitor(&item.self_ty);
179 for item in &mut item.items {
180 visitor.visit_impl_item_mut(item);
181 }
182 }
183