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