abi_stable_derive/sabi_trait/
methods_tokenizer.rs
1use super::{lifetime_unelider::BorrowKind, *};
24
25use as_derive_utils::to_token_fn::ToTokenFnMut;
26
27use quote::TokenStreamExt;
28
29#[derive(Debug, Copy, Clone)]
30pub struct MethodsTokenizer<'a> {
31 pub(crate) trait_def: &'a TraitDefinition<'a>,
32 pub(crate) which_item: WhichItem,
33}
34
35#[derive(Debug, Copy, Clone)]
36pub struct MethodTokenizer<'a> {
37 trait_def: &'a TraitDefinition<'a>,
38 method: &'a TraitMethod<'a>,
39 which_item: WhichItem,
40}
41
42impl<'a> ToTokens for MethodsTokenizer<'a> {
43 fn to_tokens(&self, ts: &mut TokenStream2) {
44 for method in &self.trait_def.methods {
45 MethodTokenizer {
46 trait_def: self.trait_def,
47 method,
48 which_item: self.which_item,
49 }
50 .to_tokens(ts);
51 }
52 }
53}
54
55impl<'a> ToTokens for MethodTokenizer<'a> {
56 fn to_tokens(&self, ts: &mut TokenStream2) {
57 let which_item = self.which_item;
58 let method = self.method;
59 let trait_def = self.trait_def;
60 let ctokens = trait_def.ctokens;
61 let (is_method, vis) = match which_item {
66 WhichItem::Trait | WhichItem::TraitImpl => (true, None),
67 WhichItem::TraitObjectImpl => (true, Some(trait_def.submod_vis)),
68 WhichItem::VtableDecl | WhichItem::VtableImpl => (false, Some(trait_def.submod_vis)),
69 };
70
71 let default_ = method
76 .default
77 .as_ref()
78 .filter(|_| !method.disable_inherent_default);
79
80 let lifetimes = Some(&method.lifetimes)
81 .filter(|l| !l.is_empty())
82 .into_iter();
83
84 let method_name = method.name;
85 let method_span = method_name.span();
86
87 let self_ty = if is_method {
88 quote_spanned!(method_span=> Self)
89 } else {
90 quote_spanned!(method_span=> _Self)
91 };
92
93 struct WriteLifetime<'a>(Option<&'a syn::Lifetime>);
94
95 impl ToTokens for WriteLifetime<'_> {
96 fn to_tokens(&self, ts: &mut TokenStream2) {
97 if let Some(lt) = self.0 {
98 lt.to_tokens(ts)
99 } else {
100 ts.append_all(quote!('_))
101 }
102 }
103 }
104
105 let self_param = match (is_method, &method.self_param) {
106 (
107 true,
108 SelfParam::ByRef {
109 lifetime,
110 is_mutable: false,
111 },
112 ) => {
113 quote_spanned!(method_span=> & #lifetime self)
114 }
115 (
116 true,
117 SelfParam::ByRef {
118 lifetime,
119 is_mutable: true,
120 },
121 ) => {
122 quote_spanned!(method_span=> & #lifetime mut self)
123 }
124 (true, SelfParam::ByVal) => {
125 quote_spanned!(method_span=> self)
126 }
127 (
128 false,
129 SelfParam::ByRef {
130 lifetime,
131 is_mutable: false,
132 },
133 ) => {
134 let lifetime = WriteLifetime(*lifetime);
135 quote_spanned!(method_span=> _self: __sabi_re::RRef<#lifetime, ()>)
136 }
137 (
138 false,
139 SelfParam::ByRef {
140 lifetime,
141 is_mutable: true,
142 },
143 ) => {
144 let lifetime = WriteLifetime(*lifetime);
145 quote_spanned!(method_span=> _self: __sabi_re::RMut<#lifetime, ()>)
146 }
147 (false, SelfParam::ByVal) => {
148 quote_spanned!(method_span=> _self:*mut ())
149 }
150 };
151
152 let param_names_a = method.params.iter().map(move |param| {
153 ToTokenFnMut::new(move |ts| match which_item {
154 WhichItem::Trait => {
155 param.pattern.to_tokens(ts);
156 }
157 _ => {
158 param.name.to_tokens(ts);
159 }
160 })
161 });
162 let param_ty = method.params.iter().map(|param| ¶m.ty);
163 let param_names_c = param_names_a.clone();
164 let param_names_d = param_names_a.clone();
165 let param_names_e = method.params.iter().map(|x| x.pattern);
166 let return_ty = method.output.iter();
167
168 let self_is_sized_bound = Some(&ctokens.self_sized)
169 .filter(|_| is_method && method.self_param == SelfParam::ByVal);
170
171 let abi = match which_item {
172 WhichItem::VtableImpl => Some(&ctokens.extern_c),
173 _ => method.abi,
174 };
175
176 let user_where_clause = method.where_clause.get_tokenizer(ctokens);
177
178 let other_attrs = if which_item == WhichItem::Trait {
179 method.other_attrs
180 } else {
181 &[]
182 };
183
184 if WhichItem::VtableImpl == which_item {
185 ts.append_all(quote_spanned!(method_span=> #[doc(hidden)] ));
186 }
187
188 if WhichItem::VtableDecl == which_item {
189 let optional_field = default_.as_ref().map(|_| &ctokens.missing_field_option);
190 let derive_attrs = method.derive_attrs;
191
192 quote_spanned!( method_span=>
193 #optional_field
194 #(#derive_attrs)*
195 #vis #method_name:
196 #(for< #(#lifetimes,)* >)*
197 unsafe extern "C" fn(
198 #self_param,
199 #( #param_names_a:#param_ty ,)*
200 ) #(-> #return_ty )*
201 )
202 } else {
203 let inherent_method_docs = ToTokenFnMut::new(|ts| {
204 if WhichItem::TraitObjectImpl != which_item {
205 return;
206 }
207 let trait_name = trait_def.name;
208 let m_docs = format!(
209 "This is the inherent equivalent of \
210 [the trait method of the same name](./trait.{TN}.html#tymethod.{TM})\
211 ",
212 TN = trait_name,
213 TM = method_name,
214 );
215
216 ts.append_all(quote!(#[doc = #m_docs]));
217 });
218
219 let unsafety = match which_item {
220 WhichItem::VtableImpl => Some(&ctokens.unsafe_),
221 _ => method.unsafety,
222 };
223
224 quote_spanned!(method_span=>
225 #[allow(clippy::let_and_return)]
226 #(#other_attrs)*
227 #inherent_method_docs
228 #vis #unsafety #abi fn #method_name #(< #(#lifetimes,)* >)* (
229 #self_param,
230 #( #param_names_a:#param_ty ,)*
231 ) #(-> #return_ty )*
232 where
233 #self_is_sized_bound
234 #user_where_clause
235 )
236 }
237 .to_tokens(ts);
238
239 let ptr_constraint = match &method.self_param {
240 SelfParam::ByRef {
241 is_mutable: false, ..
242 } => &ctokens.ptr_ref_bound,
243 SelfParam::ByRef {
244 is_mutable: true, ..
245 } => &ctokens.ptr_mut_bound,
246 SelfParam::ByVal => &ctokens.ptr_val_bound,
247 };
248
249 let output_safety = |output: &mut TokenStream2, input: TokenStream2| {
250 output.append_all(if let Some(safety) = method.unsafety {
251 quote_spanned!(safety.span => { #safety{ #input } })
252 } else {
253 quote_spanned!(method_span => { #input })
254 });
255 };
256
257 match (which_item, &method.self_param) {
258 (WhichItem::Trait, _) => {
259 method.default.as_ref().map(|x| x.block).to_tokens(ts);
260 method.semicolon.to_tokens(ts);
261 }
262 (WhichItem::TraitImpl, _) => {
263 output_safety(
264 ts,
265 quote_spanned!(method_span =>
266 self.#method_name(#(#param_names_c,)*)
267 ),
268 );
269 }
270 (WhichItem::TraitObjectImpl, _) => {
271 let method_call = match &method.self_param {
272 SelfParam::ByRef {
273 is_mutable: false, ..
274 } => {
275 quote_spanned!(method_span=>
276 __method(self.obj.sabi_as_rref(),#(#param_names_c,)*)
277 )
278 }
279 SelfParam::ByRef {
280 is_mutable: true, ..
281 } => {
282 quote_spanned!(method_span=>
283 __method(self.obj.sabi_as_rmut(),#(#param_names_c,)*)
284 )
285 }
286 SelfParam::ByVal => {
287 quote_spanned!(method_span=>
288 self.obj.sabi_with_value(
289 move|_self|__method(
290 __sabi_re::MovePtr::into_raw(_self) as *mut (),
291 #(#param_names_c,)*
292 )
293 )
294 )
295 }
296 };
297
298 match default_ {
299 Some(default_) => {
300 let block = &default_.block;
301 ts.append_all(quote_spanned!(method_span=>
302 #ptr_constraint
303 {
304 match self.sabi_vtable().#method_name() {
305 Some(__method)=>{
306 unsafe{
307 #method_call
308 }
309 }
310 None=>{
311 #(
312 let #param_names_e=#param_names_d;
313 )*
314 #block
315 }
316 }
317 }
318 ));
319 }
320 None => {
321 ts.append_all(quote_spanned!(method_span=>
322 #ptr_constraint
323 {
324 let __method=self.sabi_vtable().#method_name();
325 unsafe{
326 #method_call
327 }
328 }
329 ));
330 }
331 }
332 }
333 (WhichItem::VtableDecl, _) => {
334 quote_spanned!(method_span=> , ).to_tokens(ts);
335 }
336 (WhichItem::VtableImpl, SelfParam::ByRef { is_mutable, .. }) => {
337 let mut_token = ToTokenFnMut::new(|ts| {
338 if *is_mutable {
339 syn::token::Mut { span: method_span }.to_tokens(ts);
340 }
341 });
342
343 let ret = syn::Ident::new("ret", proc_macro2::Span::call_site());
344
345 let transmute_ret = match method.return_borrow_kind {
346 Some(BorrowKind::Reference) => {
347 quote_spanned!(method_span=> ::std::mem::transmute(#ret) )
348 }
349 Some(BorrowKind::MutReference) => {
350 quote_spanned!(method_span=> ::std::mem::transmute(#ret) )
351 }
352 Some(BorrowKind::Other) => {
353 quote_spanned!(method_span=> __sabi_re::transmute_ignore_size(#ret) )
359 }
360 None => quote_spanned!(method_span=> #ret ),
361 };
362
363 ts.append_all(quote_spanned!(method_span=>{
364 unsafe{
365 let #ret = ::abi_stable::extern_fn_panic_handling!{no_early_return;
366 __Trait::#method_name(
367 &#mut_token *_self.transmute_into_raw::<#self_ty>(),
368 #(#param_names_c,)*
369 )
370 };
371
372 #transmute_ret
373 }
374 }));
375 }
376 (WhichItem::VtableImpl, SelfParam::ByVal) => {
377 ts.append_all(quote_spanned!(method_span=>{
378 ::abi_stable::extern_fn_panic_handling!{no_early_return; unsafe{
379 __Trait::#method_name(
380 (_self as *mut #self_ty).read(),#(#param_names_c,)*
381 )
382 }}
383 }));
384 }
385 }
386 }
387}