1use serde::Serialize;
2use std::ops::ControlFlow;
3
4use super::docs::Docs;
5use super::{Attrs, Ident, Lifetime, LifetimeEnv, Mutability, PathType, TypeName};
6
7#[derive(Clone, PartialEq, Eq, Hash, Serialize, Debug)]
11#[non_exhaustive]
12pub struct Method {
13 pub name: Ident,
15
16 pub docs: Docs,
18
19 pub full_path_name: Ident,
21
22 pub self_param: Option<SelfParam>,
24
25 pub params: Vec<Param>,
27
28 pub return_type: Option<TypeName>,
30
31 pub lifetime_env: LifetimeEnv,
33
34 pub attrs: Attrs,
39}
40
41impl Method {
42 pub fn from_syn(
44 m: &syn::ImplItemFn,
45 self_path_type: PathType,
46 impl_generics: Option<&syn::Generics>,
47 impl_attrs: &Attrs,
48 ) -> Method {
49 let mut attrs = impl_attrs.clone();
50 attrs.add_attrs(&m.attrs);
51
52 let self_ident = self_path_type.path.elements.last().unwrap();
53 let method_ident = &m.sig.ident;
54 let concat_method_ident = format!("{self_ident}_{method_ident}");
55 let extern_ident = syn::Ident::new(
56 &attrs.abi_rename.apply(concat_method_ident.into()),
57 m.sig.ident.span(),
58 );
59
60 let all_params = m
61 .sig
62 .inputs
63 .iter()
64 .filter_map(|a| match a {
65 syn::FnArg::Receiver(_) => None,
66 syn::FnArg::Typed(ref t) => Some(Param::from_syn(t, self_path_type.clone())),
67 })
68 .collect::<Vec<_>>();
69
70 let self_param = m
71 .sig
72 .receiver()
73 .map(|rec| SelfParam::from_syn(rec, self_path_type.clone()));
74
75 let return_ty = match &m.sig.output {
76 syn::ReturnType::Type(_, return_typ) => {
77 Some(TypeName::from_syn(
80 return_typ.as_ref(),
81 Some(self_path_type),
82 ))
83 }
84 syn::ReturnType::Default => None,
85 };
86
87 let lifetime_env = LifetimeEnv::from_method_item(
88 m,
89 impl_generics,
90 self_param.as_ref(),
91 &all_params[..],
92 return_ty.as_ref(),
93 );
94
95 Method {
96 name: Ident::from(method_ident),
97 docs: Docs::from_attrs(&m.attrs),
98 full_path_name: Ident::from(&extern_ident),
99 self_param,
100 params: all_params,
101 return_type: return_ty,
102 lifetime_env,
103 attrs,
104 }
105 }
106
107 pub fn borrowed_params(&self) -> BorrowedParams {
128 if let Some(ref return_type) = self.return_type {
131 let lifetimes = return_type.longer_lifetimes(&self.lifetime_env);
133
134 let held_self_param = self.self_param.as_ref().filter(|self_param| {
135 if let Some((Lifetime::Named(ref name), _)) = self_param.reference {
137 if lifetimes.contains(&name) {
138 return true;
139 }
140 }
141 self_param.path_type.lifetimes.iter().any(|lt| {
142 if let Lifetime::Named(name) = lt {
143 lifetimes.contains(&name)
144 } else {
145 false
146 }
147 })
148 });
149
150 let held_params = self
153 .params
154 .iter()
155 .filter_map(|param| {
156 let mut lt_kind = LifetimeKind::ReturnValue;
157 param
158 .ty
159 .visit_lifetimes(&mut |lt, _| {
160 match lt {
164 Lifetime::Named(name) if lifetimes.contains(&name) => {
165 return ControlFlow::Break(());
166 }
167 Lifetime::Static => {
168 lt_kind = LifetimeKind::Static;
169 return ControlFlow::Break(());
170 }
171 _ => {}
172 };
173 ControlFlow::Continue(())
174 })
175 .is_break()
176 .then(|| (param, lt_kind))
177 })
178 .collect();
179
180 BorrowedParams(held_self_param, held_params)
181 } else {
182 BorrowedParams(None, vec![])
183 }
184 }
185
186 pub fn is_writeable_out(&self) -> bool {
195 let return_compatible = self
196 .return_type
197 .as_ref()
198 .map(|return_type| match return_type {
199 TypeName::Unit => true,
200 TypeName::Result(ok, _, _) => {
201 matches!(ok.as_ref(), TypeName::Unit)
202 }
203 _ => false,
204 })
205 .unwrap_or(true);
206
207 return_compatible && self.params.last().map(Param::is_writeable).unwrap_or(false)
208 }
209
210 pub fn has_writeable_param(&self) -> bool {
212 self.params.iter().any(|p| p.is_writeable())
213 }
214
215 pub fn docs(&self) -> &Docs {
217 &self.docs
218 }
219}
220
221#[derive(Clone, PartialEq, Eq, Hash, Serialize, Debug)]
223#[non_exhaustive]
224pub struct SelfParam {
225 pub reference: Option<(Lifetime, Mutability)>,
227
228 pub path_type: PathType,
231}
232
233impl SelfParam {
234 pub fn to_typename(&self) -> TypeName {
235 let typ = TypeName::Named(self.path_type.clone());
236 if let Some((ref lifetime, ref mutability)) = self.reference {
237 return TypeName::Reference(lifetime.clone(), *mutability, Box::new(typ));
238 }
239 typ
240 }
241
242 pub fn from_syn(rec: &syn::Receiver, path_type: PathType) -> Self {
243 SelfParam {
244 reference: rec
245 .reference
246 .as_ref()
247 .map(|(_, lt)| (lt.into(), Mutability::from_syn(&rec.mutability))),
248 path_type,
249 }
250 }
251}
252
253#[derive(Clone, PartialEq, Eq, Hash, Serialize, Debug)]
255#[non_exhaustive]
256pub struct Param {
257 pub name: Ident,
259
260 pub ty: TypeName,
262}
263
264impl Param {
265 pub fn is_writeable(&self) -> bool {
267 match self.ty {
268 TypeName::Reference(_, Mutability::Mutable, ref w) => **w == TypeName::Writeable,
269 _ => false,
270 }
271 }
272
273 pub fn from_syn(t: &syn::PatType, self_path_type: PathType) -> Self {
274 let ident = match t.pat.as_ref() {
275 syn::Pat::Ident(ident) => ident,
276 _ => panic!("Unexpected param type"),
277 };
278
279 Param {
280 name: (&ident.ident).into(),
281 ty: TypeName::from_syn(&t.ty, Some(self_path_type)),
282 }
283 }
284}
285
286#[derive(Debug, Copy, Clone, PartialEq, Eq)]
288pub enum LifetimeKind {
289 ReturnValue,
291 Static,
293}
294
295#[derive(Default, Debug)]
296#[non_exhaustive]
298pub struct BorrowedParams<'a>(
299 pub Option<&'a SelfParam>,
300 pub Vec<(&'a Param, LifetimeKind)>,
301);
302
303impl BorrowedParams<'_> {
304 pub fn return_names<'a>(&'a self, self_name: &'a Ident) -> impl Iterator<Item = &'a Ident> {
308 self.0.iter().map(move |_| self_name).chain(
309 self.1
310 .iter()
311 .filter(|(_, ltk)| (*ltk == LifetimeKind::ReturnValue))
312 .map(|(param, _)| ¶m.name),
313 )
314 }
315
316 pub fn static_names(&self) -> impl Iterator<Item = &'_ Ident> {
319 self.1
320 .iter()
321 .filter(|(_, ltk)| (*ltk == LifetimeKind::Static))
322 .map(|(param, _)| ¶m.name)
323 }
324
325 pub fn contains(&self, param_name: &Ident) -> bool {
331 self.1.iter().any(|(param, _)| ¶m.name == param_name)
332 }
333
334 pub fn is_empty(&self) -> bool {
336 self.0.is_none() && self.1.is_empty()
337 }
338
339 pub fn borrows_self(&self) -> bool {
341 self.0.is_some()
342 }
343
344 pub fn borrows_params(&self) -> bool {
346 !self.1.is_empty()
347 }
348
349 pub fn len(&self) -> usize {
351 self.1.len() + usize::from(self.0.is_some())
352 }
353}
354
355#[cfg(test)]
356mod tests {
357 use insta;
358
359 use syn;
360
361 use crate::ast::{Attrs, Ident, Method, Path, PathType};
362
363 #[test]
364 fn static_methods() {
365 insta::assert_yaml_snapshot!(Method::from_syn(
366 &syn::parse_quote! {
367 #[diplomat::rust_link(foo::Bar::batz, FnInStruct)]
369 fn foo(x: u64, y: MyCustomStruct) {
370
371 }
372 },
373 PathType::new(Path::empty().sub_path(Ident::from("MyStructContainingMethod"))),
374 None,
375 &Attrs::default()
376 ));
377
378 insta::assert_yaml_snapshot!(Method::from_syn(
379 &syn::parse_quote! {
380 #[diplomat::rust_link(foo::Bar::batz, FnInEnum)]
385 fn foo(x: u64, y: MyCustomStruct) -> u64 {
386 x
387 }
388 },
389 PathType::new(Path::empty().sub_path(Ident::from("MyStructContainingMethod"))),
390 None,
391 &Attrs::default()
392 ));
393 }
394
395 #[test]
396 fn cfged_method() {
397 insta::assert_yaml_snapshot!(Method::from_syn(
398 &syn::parse_quote! {
399 #[diplomat::rust_link(foo::Bar::batz, FnInStruct)]
401 #[cfg(any(feature = "foo", not(feature = "bar")))]
402 fn foo(x: u64, y: MyCustomStruct) {
403
404 }
405 },
406 PathType::new(Path::empty().sub_path(Ident::from("MyStructContainingMethod"))),
407 None,
408 &Attrs::default()
409 ));
410 }
411
412 #[test]
413 fn nonstatic_methods() {
414 insta::assert_yaml_snapshot!(Method::from_syn(
415 &syn::parse_quote! {
416 fn foo(&self, x: u64, y: MyCustomStruct) {
417
418 }
419 },
420 PathType::new(Path::empty().sub_path(Ident::from("MyStructContainingMethod"))),
421 None,
422 &Attrs::default()
423 ));
424
425 insta::assert_yaml_snapshot!(Method::from_syn(
426 &syn::parse_quote! {
427 #[diplomat::rust_link(foo::Bar::batz, FnInStruct)]
428 fn foo(&mut self, x: u64, y: MyCustomStruct) -> u64 {
429 x
430 }
431 },
432 PathType::new(Path::empty().sub_path(Ident::from("MyStructContainingMethod"))),
433 None,
434 &Attrs::default()
435 ));
436 }
437
438 macro_rules! assert_borrowed_params {
439 ([$($return_param:ident),*] $(, [$($static_param:ident),*])? => $($tokens:tt)* ) => {{
440 let method = Method::from_syn(
441 &syn::parse_quote! { $($tokens)* },
442 PathType::new(Path::empty().sub_path(Ident::from("MyStructContainingMethod"))),
443 None,
444 &Attrs::default()
445 );
446
447 let borrowed_params = method.borrowed_params();
448 let mut actual_return: Vec<&str> = borrowed_params.return_names(&Ident::THIS).map(|ident| ident.as_str()).collect();
451 if borrowed_params.0.is_some() {
452 actual_return[0] = "self";
453 }
454 let expected_return: &[&str] = &[$(stringify!($return_param)),*];
455 assert_eq!(actual_return, expected_return);
456 let actual_static: Vec<&str> = borrowed_params.static_names().map(|ident| ident.as_str()).collect();
457 let expected_static: &[&str] = &[$($(stringify!($static_param)),*)?];
458 assert_eq!(actual_static, expected_static);
459 }};
460 }
461
462 #[test]
463 fn static_params_held_by_return_type() {
464 assert_borrowed_params! { [first, second] =>
465 #[diplomat::rust_link(foo::Bar::batz, FnInStruct)]
466 fn foo<'a, 'b>(first: &'a First, second: &'b Second, third: &Third) -> Foo<'a, 'b> {
467 unimplemented!()
468 }
469 }
470
471 assert_borrowed_params! { [hold] =>
472 #[diplomat::rust_link(Foo, FnInStruct)]
473 fn transitivity<'a, 'b: 'a, 'c: 'b, 'd: 'c, 'e: 'd, 'x>(hold: &'x One<'e>, nohold: &One<'x>) -> Box<Foo<'a>> {
474 unimplemented!()
475 }
476 }
477
478 assert_borrowed_params! { [hold] =>
479 #[diplomat::rust_link(Foo, FnInStruct)]
480 fn a_le_b_and_b_le_a<'a: 'b, 'b: 'a>(hold: &'b Bar, nohold: &'c Bar) -> Box<Foo<'a>> {
481 unimplemented!()
482 }
483 }
484
485 assert_borrowed_params! { [a, b, c, d] =>
486 #[diplomat::rust_link(Foo, FnInStruct)]
487 fn many_dependents<'a, 'b: 'a, 'c: 'a, 'd: 'b, 'x, 'y>(a: &'x One<'a>, b: &'b One<'a>, c: &Two<'x, 'c>, d: &'x Two<'d, 'y>, nohold: &'x Two<'x, 'y>) -> Box<Foo<'a>> {
488 unimplemented!()
489 }
490 }
491
492 assert_borrowed_params! { [hold] =>
493 #[diplomat::rust_link(Foo, FnInStruct)]
494 fn return_outlives_param<'short, 'long: 'short>(hold: &Two<'long, 'short>, nohold: &'short One<'short>) -> Box<Foo<'long>> {
495 unimplemented!()
496 }
497 }
498
499 assert_borrowed_params! { [hold] =>
500 #[diplomat::rust_link(Foo, FnInStruct)]
501 fn transitivity_deep_types<'a, 'b: 'a, 'c: 'b, 'd: 'c>(hold: Option<Box<Bar<'d>>>, nohold: &'a Box<Option<Baz<'a>>>) -> Result<Box<Foo<'b>>, Error> {
502 unimplemented!()
503 }
504 }
505
506 assert_borrowed_params! { [top, left, right, bottom] =>
507 #[diplomat::rust_link(Foo, FnInStruct)]
508 fn diamond_top<'top, 'left: 'top, 'right: 'top, 'bottom: 'left + 'right>(top: One<'top>, left: One<'left>, right: One<'right>, bottom: One<'bottom>) -> Box<Foo<'top>> {
509 unimplemented!()
510 }
511 }
512
513 assert_borrowed_params! { [left, bottom] =>
514 #[diplomat::rust_link(Foo, FnInStruct)]
515 fn diamond_left<'top, 'left: 'top, 'right: 'top, 'bottom: 'left + 'right>(top: One<'top>, left: One<'left>, right: One<'right>, bottom: One<'bottom>) -> Box<Foo<'left>> {
516 unimplemented!()
517 }
518 }
519
520 assert_borrowed_params! { [right, bottom] =>
521 #[diplomat::rust_link(Foo, FnInStruct)]
522 fn diamond_right<'top, 'left: 'top, 'right: 'top, 'bottom: 'left + 'right>(top: One<'top>, left: One<'left>, right: One<'right>, bottom: One<'bottom>) -> Box<Foo<'right>> {
523 unimplemented!()
524 }
525 }
526
527 assert_borrowed_params! { [bottom] =>
528 #[diplomat::rust_link(Foo, FnInStruct)]
529 fn diamond_bottom<'top, 'left: 'top, 'right: 'top, 'bottom: 'left + 'right>(top: One<'top>, left: One<'left>, right: One<'right>, bottom: One<'bottom>) -> Box<Foo<'bottom>> {
530 unimplemented!()
531 }
532 }
533
534 assert_borrowed_params! { [a, b, c, d] =>
535 #[diplomat::rust_link(Foo, FnInStruct)]
536 fn diamond_and_nested_types<'a, 'b: 'a, 'c: 'b, 'd: 'b + 'c, 'x, 'y>(a: &'x One<'a>, b: &'y One<'b>, c: &One<'c>, d: &One<'d>, nohold: &One<'x>) -> Box<Foo<'a>> {
537 unimplemented!()
538 }
539 }
540 }
541
542 #[test]
543 fn nonstatic_params_held_by_return_type() {
544 assert_borrowed_params! { [self] =>
545 #[diplomat::rust_link(foo::Bar::batz, FnInStruct)]
546 fn foo<'a>(&'a self) -> Foo<'a> {
547 unimplemented!()
548 }
549 }
550
551 assert_borrowed_params! { [self, foo, bar] =>
552 #[diplomat::rust_link(foo::Bar::batz, FnInStruct)]
553 fn foo<'x, 'y>(&'x self, foo: &'x Foo, bar: &Bar<'y>, baz: &Baz) -> Foo<'x, 'y> {
554 unimplemented!()
555 }
556 }
557
558 assert_borrowed_params! { [self, bar] =>
559 #[diplomat::rust_link(foo::Bar::batz, FnInStruct)]
560 fn foo<'a, 'b>(&'a self, bar: Bar<'b>) -> Foo<'a, 'b> {
561 unimplemented!()
562 }
563 }
564
565 assert_borrowed_params! { [self, bar], [baz] =>
566 #[diplomat::rust_link(foo::Bar::batz, FnInStruct)]
567 fn foo<'a, 'b>(&'a self, bar: Bar<'b>, baz: &'static str) -> Foo<'a, 'b, 'static> {
568 unimplemented!()
569 }
570 }
571 }
572}