1#![doc = include_str!("../TYPES.md")]
185#![cfg_attr(not(test), deny(clippy::unwrap_used))]
187#![cfg_attr(not(test), deny(clippy::allow_attributes_without_reason))]
189
190use proc_macro::TokenStream;
191use quote::quote;
192use rsbinder_aidl::render::{render_interface, FnMembers, InterfaceRender, TransactionWrite};
193use syn::{
194 parse::{Parse, ParseStream},
195 punctuated::Punctuated,
196 FnArg, ItemTrait, Pat, PathArguments, ReturnType, Token, TraitItem, TraitItemFn, Type,
197};
198
199mod aidl_shape;
200mod binder_enum;
201mod parcelable;
202mod service_error;
203#[cfg(test)]
204mod type_matrix;
205mod type_str;
206
207#[derive(Clone, Copy, PartialEq, Eq)]
209enum Dir {
210 In,
211 Out,
212 Inout,
213}
214
215struct Args {
217 descriptor: Option<String>,
218}
219
220impl Parse for Args {
221 fn parse(input: ParseStream) -> syn::Result<Self> {
222 let mut descriptor = None;
223 if input.is_empty() {
224 return Ok(Self { descriptor });
225 }
226 let metas = Punctuated::<syn::MetaNameValue, Token![,]>::parse_terminated(input)?;
227 for meta in metas {
228 if meta.path.is_ident("descriptor") {
229 if descriptor.is_some() {
230 return Err(syn::Error::new_spanned(
231 &meta.path,
232 "`descriptor` is given more than once",
233 ));
234 }
235 let syn::Expr::Lit(syn::ExprLit {
236 lit: syn::Lit::Str(s),
237 ..
238 }) = &meta.value
239 else {
240 return Err(syn::Error::new_spanned(
241 &meta.value,
242 "descriptor must be a string literal",
243 ));
244 };
245 let value = s.value();
247 if let Some(bad) = value.chars().find(|c| "\\\"\n\r".contains(*c)) {
248 return Err(syn::Error::new_spanned(
249 s,
250 format!(
251 "a descriptor cannot contain {bad:?} — it is written verbatim \
252 into the generated source and onto the wire"
253 ),
254 ));
255 }
256 if value.is_empty() {
258 return Err(syn::Error::new_spanned(
259 s,
260 "a descriptor cannot be empty — it is the wire name a peer compares, \
261 and `.aidl` always has a name to put there",
262 ));
263 }
264 descriptor = Some(value);
265 } else {
266 return Err(syn::Error::new_spanned(
267 &meta.path,
268 "unknown argument; only `descriptor = \"…\"` is supported",
269 ));
270 }
271 }
272 Ok(Self { descriptor })
273 }
274}
275
276fn attr_descriptor(attrs: &[syn::Attribute], name: &str) -> syn::Result<Option<String>> {
278 let mut found = None;
279 for attr in attrs {
280 if !attr.path().is_ident(name) {
281 continue;
282 }
283 let args: Args = attr.parse_args()?;
284 if args.descriptor.is_some() {
285 if found.is_some() {
286 return Err(syn::Error::new_spanned(
287 attr,
288 format!("`descriptor` is given more than once; #[{name}(..)] appears twice"),
289 ));
290 }
291 found = args.descriptor;
292 }
293 }
294 Ok(found)
295}
296
297#[proc_macro_derive(Parcelable, attributes(parcelable))]
336pub fn derive_parcelable(item: TokenStream) -> TokenStream {
337 let input = syn::parse_macro_input!(item as syn::DeriveInput);
338 match parcelable::expand(&input) {
339 Ok(ts) => ts.into(),
340 Err(e) => e.to_compile_error().into(),
341 }
342}
343
344#[proc_macro_derive(BinderEnum)]
371pub fn derive_binder_enum(item: TokenStream) -> TokenStream {
372 let input = syn::parse_macro_input!(item as syn::DeriveInput);
373 match binder_enum::expand(&input) {
374 Ok(ts) => ts.into(),
375 Err(e) => e.to_compile_error().into(),
376 }
377}
378
379#[proc_macro_derive(ServiceSpecificError)]
409pub fn derive_service_specific_error(item: TokenStream) -> TokenStream {
410 let input = syn::parse_macro_input!(item as syn::DeriveInput);
411 match service_error::expand(&input) {
412 Ok(ts) => ts.into(),
413 Err(e) => e.to_compile_error().into(),
414 }
415}
416
417#[proc_macro_attribute]
439pub fn interface(attr: TokenStream, item: TokenStream) -> TokenStream {
440 let args = syn::parse_macro_input!(attr as Args);
441 let item_trait = syn::parse_macro_input!(item as ItemTrait);
442 match expand(&args, &item_trait) {
443 Ok(ts) => ts.into(),
444 Err(e) => e.to_compile_error().into(),
445 }
446}
447
448fn expand(args: &Args, item: &ItemTrait) -> syn::Result<proc_macro2::TokenStream> {
449 let rendered = render_source(args, item)?;
450
451 let file = syn::parse_file(&rendered).map_err(|e| {
452 syn::Error::new_spanned(
453 &item.ident,
454 format!("generated code did not parse ({e}); generated source follows:\n{rendered}"),
455 )
456 })?;
457 let mut items = file.items.into_iter();
458 let (Some(syn::Item::Mod(mut module)), None) = (items.next(), items.next()) else {
459 return Err(syn::Error::new_spanned(
460 &item.ident,
461 "generated code was not a single module (generator contract changed)",
462 ));
463 };
464
465 let raw_name = item.ident.to_string();
467 let mod_ident = syn::Ident::new(
468 &format!("{}_binder", strip_raw(&raw_name)),
469 item.ident.span(),
470 );
471 module.ident = mod_ident.clone();
472 module.vis = item.vis.clone();
474
475 let copied = by_value_types(item)?;
477 if let Some((_, items)) = module.content.as_mut() {
478 items.insert(
479 0,
480 syn::parse_quote!(
481 use super::*;
482 ),
483 );
484 for ty in &copied {
485 items.push(syn::parse_quote!(
486 const _: fn() = || {
487 fn __rsbinder_assert_copy<T: ::core::marker::Copy>() {}
488 __rsbinder_assert_copy::<#ty>();
489 };
490 ));
491 }
492 }
493
494 let marker = syn::Ident::new(
496 &format!(
497 "__rsbinder_one_interface_per_stem_{}",
498 rsbinder_aidl::render::interface_stem(strip_raw(&raw_name))
499 ),
500 item.ident.span(),
501 );
502
503 let vis = &item.vis;
504 Ok(quote! {
505 #[doc(hidden)]
506 #module
507 #vis use #mod_ident::*;
508 #[doc(hidden)]
509 #[allow(dead_code, non_upper_case_globals)]
510 const #marker: () = ();
511 })
512}
513
514pub(crate) fn strip_raw(ident: &str) -> &str {
516 ident.strip_prefix("r#").unwrap_or(ident)
517}
518
519fn render_source(args: &Args, item: &ItemTrait) -> syn::Result<String> {
521 render_source_with(args, item, cfg!(feature = "async"))
522}
523
524fn render_source_with(args: &Args, item: &ItemTrait, enabled_async: bool) -> syn::Result<String> {
525 check_attrs(&item.attrs, &["deprecated"])?;
527 if let Some(unsafety) = &item.unsafety {
529 return Err(syn::Error::new_spanned(
530 unsafety,
531 "an `unsafe` binder interface is not supported — the generated trait is safe, \
532 so the obligation would be silently dropped",
533 ));
534 }
535 if let Some(auto_token) = &item.modifiers.auto_token {
536 return Err(syn::Error::new_spanned(
537 auto_token,
538 "an `auto` trait carries no methods and cannot be a binder interface",
539 ));
540 }
541 item.modifiers.require_empty()?;
543 if let Some(where_clause) = &item.generics.where_clause {
544 return Err(syn::Error::new_spanned(
545 where_clause,
546 "a where clause has no meaning on a binder interface — the generated trait \
547 requires only `rsbinder::Interface + Send`",
548 ));
549 }
550 if !item.generics.params.is_empty() {
551 return Err(syn::Error::new_spanned(
552 &item.generics,
553 "a binder interface cannot be generic — the wire has no way to carry the parameter",
554 ));
555 }
556 if !item.supertraits.is_empty() {
557 return Err(syn::Error::new_spanned(
558 &item.supertraits,
559 "supertraits are not supported; the generated trait already requires \
560 `rsbinder::Interface + Send`",
561 ));
562 }
563
564 type_str::reject_reserved_ident(&item.ident)?;
565
566 let raw = item.ident.to_string();
567 let name = strip_raw(&raw).to_string();
568 let mut fn_members = Vec::new();
569 let mut seen = std::collections::HashSet::new();
571 for (i, trait_item) in item.items.iter().enumerate() {
572 let TraitItem::Fn(f) = trait_item else {
573 return Err(syn::Error::new_spanned(
574 trait_item,
575 "only methods are supported; constants and associated types need `.aidl`",
576 ));
577 };
578 let ident = f.sig.ident.to_string();
579 if !seen.insert(strip_raw(&ident).to_string()) {
580 return Err(syn::Error::new_spanned(
581 &f.sig.ident,
582 format!("duplicate method name `{}`", strip_raw(&ident)),
583 ));
584 }
585 let bare = strip_raw(&ident);
587 if matches!(
588 bare,
589 "descriptor" | "getDefaultImpl" | "setDefaultImpl" | "dump" | "as_binder"
590 ) {
591 return Err(syn::Error::new_spanned(
592 &f.sig.ident,
593 format!("method `{bare}` collides with an item of the generated Rust trait"),
594 ));
595 }
596 let args: Vec<&Type> = f
598 .sig
599 .inputs
600 .iter()
601 .filter_map(|a| match a {
602 FnArg::Typed(t) => Some(&*t.ty),
603 FnArg::Receiver(_) => None,
604 })
605 .collect();
606 let reserved = match (bare, args.as_slice()) {
607 ("asBinder" | "getInterfaceHash" | "getInterfaceVersion", []) => true,
608 ("getTransactionName", [ty]) => matches!(
609 type_str::unwrap_group(ty),
610 Type::Path(p) if p.qself.is_none()
611 && p.path.segments.last().is_some_and(|s| s.ident == "i32" && s.arguments.is_none())
612 ),
613 _ => false,
614 };
615 if reserved {
616 return Err(syn::Error::new_spanned(
617 &f.sig.ident,
618 format!("method `{bare}` is reserved for internal use"),
619 ));
620 }
621 fn_members.push(make_fn_member(f, i as u32)?);
622 }
623
624 let descriptor = args.descriptor.clone().unwrap_or_else(|| name.clone());
625 let mut render = InterfaceRender::new(name, descriptor);
626 render.fn_members = fn_members;
627 render.enabled_async = enabled_async;
628 render.deprecated = deprecated_of(&item.attrs)?;
629
630 render_interface(&render)
631 .map(|s| s.trim().to_string())
632 .map_err(|e| syn::Error::new_spanned(&item.ident, format!("codegen failed: {e}")))
633}
634
635fn make_fn_member(f: &TraitItemFn, index: u32) -> syn::Result<FnMembers> {
636 f.modifiers.require_empty()?;
638 if f.default.is_some() {
639 return Err(syn::Error::new_spanned(
640 &f.sig.ident,
641 "a default body has no meaning on a binder interface — the server `impl` provides it",
642 ));
643 }
644 if !f.sig.generics.params.is_empty() {
645 return Err(syn::Error::new_spanned(
646 &f.sig.generics,
647 "generic methods are not supported",
648 ));
649 }
650 if let Some(where_clause) = &f.sig.generics.where_clause {
651 return Err(syn::Error::new_spanned(
652 where_clause,
653 "a where clause on a binder method is not supported",
654 ));
655 }
656 if let Some(tok) = &f.sig.asyncness {
658 return Err(syn::Error::new_spanned(
659 tok,
660 "declare the method as sync; the `async` feature emits the `IFooAsync` halves \
661 alongside it",
662 ));
663 }
664 if !matches!(f.sig.safety, syn::Safety::Default) {
666 return Err(syn::Error::new_spanned(
667 &f.sig.safety,
668 "an `unsafe` binder method is not supported",
669 ));
670 }
671 if let Some(tok) = &f.sig.constness {
672 return Err(syn::Error::new_spanned(
673 tok,
674 "a `const` binder method is not supported",
675 ));
676 }
677 if let Some(abi) = &f.sig.abi {
678 return Err(syn::Error::new_spanned(
679 abi,
680 "an explicit ABI has no meaning on a binder method",
681 ));
682 }
683 if let Some(variadic) = &f.sig.variadic {
685 return Err(syn::Error::new_spanned(
686 variadic,
687 "a variadic binder method is not supported — the wire carries a fixed \
688 argument list",
689 ));
690 }
691 let oneway = has_attr(&f.attrs, "oneway");
692 check_attrs(&f.attrs, &["oneway", "deprecated"])?;
693
694 const SHARED_RECEIVER: &str = "the first parameter must be `&self` — a binder object is \
695 shared, never owned or uniquely borrowed by a call";
696
697 let mut inputs = f.sig.inputs.iter();
698 let Some(FnArg::Receiver(receiver)) = inputs.next() else {
699 return Err(syn::Error::new_spanned(&f.sig, SHARED_RECEIVER));
700 };
701 let syn::ReceiverKind::Reference(_, lifetime, None) = &receiver.kind else {
702 return Err(syn::Error::new_spanned(&f.sig, SHARED_RECEIVER));
703 };
704 if let Some(lifetime) = lifetime.as_ref().filter(|lt| lt.ident != "_") {
706 return Err(syn::Error::new_spanned(
707 lifetime,
708 "an explicit lifetime on `self` is not supported — the generated trait takes a \
709 plain `&self`",
710 ));
711 }
712 check_attrs(&receiver.attrs, &[])?;
713
714 let mut args = "&self".to_string();
715 let mut args_async = "&'a self".to_string();
716 let mut func_call_params = String::new();
717 let mut write_funcs = Vec::new();
718 let mut transaction_decls = Vec::new();
719 let mut transaction_write = Vec::new();
720 let mut transaction_params = String::new();
721 let mut read_onto_params = Vec::new();
722 let mut arg_names = std::collections::HashSet::new();
723
724 for input in inputs {
725 let FnArg::Typed(pat_ty) = input else {
726 return Err(syn::Error::new_spanned(input, "unexpected receiver"));
727 };
728 let Pat::Ident(pat_ident) = &*pat_ty.pat else {
729 return Err(syn::Error::new_spanned(
730 &pat_ty.pat,
731 "parameter patterns are not supported; use a plain name",
732 ));
733 };
734 check_attrs(&pat_ty.attrs, &["inout"])?;
736 let raw_arg = pat_ident.ident.to_string();
737 let ident = format!("_arg_{}", strip_raw(&raw_arg));
738 if !arg_names.insert(ident.clone()) {
740 return Err(syn::Error::new_spanned(
741 &pat_ident.ident,
742 format!("duplicate argument name `{}`", strip_raw(&raw_arg)),
743 ));
744 }
745 let dir = direction(&pat_ty.attrs, &pat_ty.ty)?;
746 if oneway && dir != Dir::In {
747 return Err(syn::Error::new_spanned(
748 &pat_ty.ty,
749 "a #[oneway] method cannot have an out/inout parameter — there is no reply to \
750 write it back into",
751 ));
752 }
753 type_str::check_type_at(
754 &pat_ty.ty,
755 match dir {
756 Dir::In => type_str::Place::In,
757 Dir::Out => type_str::Place::Out,
758 Dir::Inout => type_str::Place::Inout,
759 },
760 )?;
761 type_str::reject_reserved_names(&pat_ty.ty)?;
762 let as_written = type_str::as_written(&pat_ty.ty)?;
763 let owned = type_str::owned(&pat_ty.ty)?;
764
765 let arg_str = format!(", {ident}: {as_written}");
766 args += &arg_str;
767 args_async += &arg_str.replace('&', "&'a ");
768 func_call_params += &format!("{ident}, ");
769
770 let is_option_vec = type_str::option_vec_elem(&pat_ty.ty).is_some();
772 let is_var_array = type_str::is_variable_array(&pat_ty.ty);
773 if dir != Dir::Out {
774 let param = if as_written.starts_with('&') {
775 ident.clone()
776 } else {
777 format!("&{ident}")
778 };
779 write_funcs.push(format!("data.write({param})?;"));
780 } else if is_option_vec {
781 write_funcs.push(format!("data.write_slice_size({ident}.as_deref())?;"));
782 } else if is_var_array {
783 write_funcs.push(format!(
785 "data.write_slice_size(::core::option::Option::Some({ident}))?;"
786 ));
787 }
788
789 let (mutable, init) = match dir {
790 Dir::Out => (
791 "mut ",
792 type_str::out_default(&pat_ty.ty)?
793 .unwrap_or_else(|| "::core::default::Default::default()".into()),
794 ),
795 Dir::Inout => ("mut ", "_reader.read()?".to_string()),
796 Dir::In => ("", "_reader.read()?".to_string()),
797 };
798 transaction_decls.push(format!("let {mutable}{ident}: {owned} = {init};"));
799 if dir == Dir::Out {
800 if is_option_vec {
801 transaction_decls.push(format!("_reader.resize_nullable_out_vec(&mut {ident})?;"));
802 } else if is_var_array {
803 transaction_decls.push(format!("_reader.resize_out_vec(&mut {ident})?;"));
804 }
805 }
806
807 if dir != Dir::In {
808 let mut write = TransactionWrite::new(ident.clone());
809 if dir == Dir::Out {
811 if let Some(flatten) = type_str::out_pfd_null_guard(&pat_ty.ty) {
812 write.needs_null_guard = true;
813 write.null_guard_flatten = flatten;
814 }
815 }
816 transaction_write.push(write);
817 read_onto_params.push(ident.clone());
818 }
819 transaction_params += &format!(
820 "{}, ",
821 func_call_param(&ident, &pat_ty.ty, &as_written, &owned, dir)
822 );
823 }
824
825 let return_type = return_type(f)?;
826 if oneway && return_type != "()" {
827 return Err(syn::Error::new_spanned(
828 &f.sig.output,
829 "a #[oneway] method cannot return a value — there is no reply to read it from",
830 ));
831 }
832
833 let raw_ident = f.sig.ident.to_string();
834 let mut member = FnMembers::new(strip_raw(&raw_ident), index);
835 member.args = args;
836 member.args_async = args_async;
837 member.transaction_has_return = return_type != "()";
838 member.return_type = return_type;
839 member.write_funcs = write_funcs;
840 member.func_call_params = trim_comma(func_call_params);
841 member.transaction_decls = transaction_decls;
842 member.transaction_write = transaction_write;
843 member.transaction_params = trim_comma(transaction_params);
844 member.oneway = oneway;
845 member.read_onto_params = read_onto_params;
846 member.deprecated = deprecated_of(&f.attrs)?;
847 Ok(member)
848}
849
850fn func_call_param(ident: &str, ty: &Type, as_written: &str, owned: &str, dir: Dir) -> String {
852 if dir != Dir::In {
853 return format!("&mut {ident}");
854 }
855 if as_written == owned {
856 return ident.to_string();
857 }
858 if as_written == "&str" {
859 return format!("{ident}.as_str()");
860 }
861 if as_written.starts_with('&') {
862 return format!("&{ident}");
863 }
864 let borrowed = type_str::option_inner(ty).map(type_str::unwrap_group);
866 match borrowed {
867 Some(Type::Reference(r)) => match type_str::unwrap_group(&r.elem) {
868 Type::Path(p) if p.path.is_ident("str") => format!("{ident}.as_deref()"),
869 Type::Slice(_) => format!("{ident}.as_deref()"),
870 _ => format!("{ident}.as_ref()"),
871 },
872 _ => format!("&{ident}"),
873 }
874}
875
876fn direction(attrs: &[syn::Attribute], ty: &Type) -> syn::Result<Dir> {
877 let is_mut_ref =
878 matches!(type_str::unwrap_group(ty), Type::Reference(r) if r.mutability.is_some());
879 if has_attr(attrs, "inout") {
880 if !is_mut_ref {
881 return Err(syn::Error::new_spanned(
882 ty,
883 "#[inout] needs `&mut T` — the callee has to be able to write it back",
884 ));
885 }
886 return Ok(Dir::Inout);
887 }
888 Ok(if is_mut_ref { Dir::Out } else { Dir::In })
889}
890
891fn return_type(f: &TraitItemFn) -> syn::Result<String> {
892 let ReturnType::Type(_, ty) = &f.sig.output else {
893 return Err(syn::Error::new_spanned(
894 &f.sig,
895 "a binder method must return `BinderResult<T>` — every call can fail on the wire",
896 ));
897 };
898 let Type::Path(p) = type_str::unwrap_group(ty) else {
899 return Err(syn::Error::new_spanned(ty, "expected `BinderResult<T>`"));
900 };
901 let last = p.path.segments.last().expect("non-empty path");
902 if last.ident != "BinderResult" {
903 return Err(syn::Error::new_spanned(
904 ty,
905 "expected `BinderResult<T>` (aliased as `rsbinder::BinderResult`)",
906 ));
907 }
908 let PathArguments::AngleBracketed(args) = &last.arguments else {
909 return Err(syn::Error::new_spanned(
910 ty,
911 "`BinderResult` needs its success type: `BinderResult<()>` for no return",
912 ));
913 };
914 if args.args.len() != 1 {
915 return Err(syn::Error::new_spanned(
916 ty,
917 "`BinderResult` takes exactly one type argument",
918 ));
919 }
920 let Some(syn::GenericArgument::Type(inner)) = args.args.first() else {
921 return Err(syn::Error::new_spanned(ty, "expected `BinderResult<T>`"));
922 };
923 type_str::check_type_at(inner, type_str::Place::Return)?;
924 type_str::reject_reserved_names(inner)?;
925 type_str::owned(inner)
926}
927
928fn by_value_types(item: &ItemTrait) -> syn::Result<Vec<Type>> {
930 let mut out = Vec::new();
931 for trait_item in &item.items {
932 let TraitItem::Fn(f) = trait_item else {
933 continue;
934 };
935 for input in &f.sig.inputs {
936 let FnArg::Typed(pat_ty) = input else {
937 continue;
938 };
939 if !type_str::as_written(&pat_ty.ty)?.starts_with('&') {
940 out.push((*pat_ty.ty).clone());
941 }
942 }
943 }
944 Ok(out)
945}
946
947pub(crate) fn deprecated_of(attrs: &[syn::Attribute]) -> syn::Result<String> {
949 let mut found = attrs.iter().filter(|a| a.path().is_ident("deprecated"));
950 let Some(attr) = found.next() else {
951 return Ok(String::new());
952 };
953 if let Some(second) = found.next() {
955 return Err(syn::Error::new_spanned(
956 second,
957 "#[deprecated] is given more than once",
958 ));
959 }
960 match &attr.meta {
961 syn::Meta::Path(_) => Ok(rsbinder_aidl::render::deprecated_attr(Some(&String::new()))),
962 syn::Meta::NameValue(nv) => {
963 let syn::Expr::Lit(syn::ExprLit {
964 lit: syn::Lit::Str(s),
965 ..
966 }) = &nv.value
967 else {
968 return Err(syn::Error::new_spanned(
969 &nv.value,
970 "a deprecation note must be a string literal",
971 ));
972 };
973 let note = s.value();
974 Ok(rsbinder_aidl::render::deprecated_attr(Some(¬e)))
975 }
976 syn::Meta::List(list) => Err(syn::Error::new_spanned(
977 list,
978 "`.aidl` carries only `#[deprecated]` and `#[deprecated = \"…\"]`; the other fields \
979 of the Rust attribute have no AIDL form and would be dropped",
980 )),
981 }
982}
983
984fn has_attr(attrs: &[syn::Attribute], name: &str) -> bool {
985 attrs.iter().any(|a| a.path().is_ident(name))
986}
987
988fn check_attrs(attrs: &[syn::Attribute], allowed: &[&str]) -> syn::Result<()> {
990 for attr in attrs {
991 if attr.path().is_ident("doc") && matches!(attr.meta, syn::Meta::NameValue(_)) {
993 continue;
994 }
995 if let Some(name) = allowed.iter().find(|a| attr.path().is_ident(a)) {
996 if *name != "deprecated" && !matches!(attr.meta, syn::Meta::Path(_)) {
998 return Err(syn::Error::new_spanned(
999 attr,
1000 format!("#[{name}] takes no arguments"),
1001 ));
1002 }
1003 continue;
1004 }
1005 let expected = if allowed.is_empty() {
1006 "no attribute is supported here".to_string()
1007 } else {
1008 format!(
1009 "#[rsbinder::interface] understands only {} here",
1010 allowed
1011 .iter()
1012 .map(|a| format!("#[{a}]"))
1013 .collect::<Vec<_>>()
1014 .join(", ")
1015 )
1016 };
1017 return Err(syn::Error::new_spanned(
1018 attr,
1019 format!("unsupported attribute; {expected}"),
1020 ));
1021 }
1022 Ok(())
1023}
1024
1025fn trim_comma(mut s: String) -> String {
1026 if s.ends_with(", ") {
1027 s.truncate(s.len() - 2);
1028 }
1029 s
1030}
1031
1032#[cfg(test)]
1033mod golden {
1034 use super::*;
1037 use std::path::{Path, PathBuf};
1038 use std::sync::atomic::{AtomicU32, Ordering};
1039
1040 struct TempDir(PathBuf);
1042
1043 impl TempDir {
1044 fn new(tag: &str) -> Self {
1045 static COUNTER: AtomicU32 = AtomicU32::new(0);
1046 let n = COUNTER.fetch_add(1, Ordering::Relaxed);
1047 let dir = std::env::temp_dir().join(format!(
1048 "rsbinder_macros_golden_{tag}_{}_{n}",
1049 std::process::id()
1050 ));
1051 let _ = std::fs::remove_dir_all(&dir);
1052 std::fs::create_dir_all(&dir).expect("temp dir");
1053 Self(dir)
1054 }
1055
1056 fn path(&self) -> &Path {
1057 &self.0
1058 }
1059 }
1060
1061 impl Drop for TempDir {
1062 fn drop(&mut self) {
1063 let _ = std::fs::remove_dir_all(&self.0);
1064 }
1065 }
1066
1067 fn from_aidl(aidl: &str, module: &str, enabled_async: bool) -> String {
1069 let dir = TempDir::new(module);
1070 let src = dir.path().join(format!("{module}.aidl"));
1071 std::fs::write(&src, aidl).expect("write aidl");
1072
1073 rsbinder_aidl::Builder::new()
1074 .source(src)
1075 .include_dir(dir.path())
1076 .dest_dir(dir.path())
1077 .output(PathBuf::from("golden.rs"))
1078 .set_async_support(enabled_async)
1079 .generate()
1080 .expect("aidl generate");
1081
1082 let text = std::fs::read_to_string(dir.path().join("golden.rs")).expect("read generated");
1083 extract_module(&text, module)
1084 }
1085
1086 fn extract_module(text: &str, module: &str) -> String {
1088 let needle = format!("pub mod {module} {{");
1089 let start_line = text
1090 .lines()
1091 .position(|l| l.trim_start().starts_with(&needle))
1092 .unwrap_or_else(|| panic!("no `pub mod {module}` in:\n{text}"));
1093 let lines: Vec<&str> = text.lines().collect();
1094 let indent = lines[start_line].len() - lines[start_line].trim_start().len();
1095
1096 let mut depth = 0usize;
1097 let mut end_line = start_line;
1098 for (i, line) in lines.iter().enumerate().skip(start_line) {
1099 depth += line.matches('{').count();
1100 depth -= line.matches('}').count();
1101 if depth == 0 {
1102 end_line = i;
1103 break;
1104 }
1105 }
1106
1107 lines[start_line..=end_line]
1108 .iter()
1109 .map(|l| {
1110 if l.len() >= indent {
1111 &l[indent..]
1112 } else {
1113 l.trim_start()
1114 }
1115 })
1116 .collect::<Vec<_>>()
1117 .join("\n")
1118 .trim_end()
1119 .to_string()
1120 }
1121
1122 fn from_macro(tokens: proc_macro2::TokenStream, enabled_async: bool) -> String {
1123 let item: ItemTrait = syn::parse2(tokens).expect("parse trait");
1124 render_source_with(&Args { descriptor: None }, &item, enabled_async).expect("render")
1125 }
1126
1127 #[track_caller]
1129 fn assert_same(aidl: &str, module: &str, tokens: proc_macro2::TokenStream) {
1130 for enabled_async in [false, true] {
1131 assert_same_for(aidl, module, tokens.clone(), enabled_async);
1132 }
1133 }
1134
1135 #[track_caller]
1136 fn assert_same_for(
1137 aidl: &str,
1138 module: &str,
1139 tokens: proc_macro2::TokenStream,
1140 enabled_async: bool,
1141 ) {
1142 let expected = from_aidl(aidl, module, enabled_async);
1143 let actual = from_macro(tokens, enabled_async);
1144 if expected != actual {
1145 let mut report = String::new();
1146 for diff in expected
1147 .lines()
1148 .zip(actual.lines())
1149 .enumerate()
1150 .filter(|(_, (a, b))| a != b)
1151 {
1152 let (i, (a, b)) = diff;
1153 report.push_str(&format!("line {i}:\n aidl : {a}\n macro: {b}\n"));
1154 }
1155 if expected.lines().count() != actual.lines().count() {
1156 report.push_str(&format!(
1157 "line count: aidl {} vs macro {}\n",
1158 expected.lines().count(),
1159 actual.lines().count()
1160 ));
1161 }
1162 panic!("generated code differs (enabled_async = {enabled_async})\n{report}\n--- aidl ---\n{expected}\n--- macro ---\n{actual}");
1163 }
1164 }
1165
1166 #[track_caller]
1168 fn assert_same_files(
1169 files: &[(&str, &str)],
1170 main: &str,
1171 module: &str,
1172 descriptor: &str,
1173 tokens: proc_macro2::TokenStream,
1174 ) {
1175 for enabled_async in [false, true] {
1176 let expected = from_aidl_files(files, main, module, enabled_async);
1177 let item: ItemTrait = syn::parse2(tokens.clone()).expect("parse trait");
1178 let actual = render_source_with(
1179 &Args {
1180 descriptor: Some(descriptor.to_string()),
1181 },
1182 &item,
1183 enabled_async,
1184 )
1185 .expect("render");
1186 assert_eq!(
1187 expected, actual,
1188 "generated code differs (enabled_async = {enabled_async})"
1189 );
1190 }
1191 }
1192
1193 pub(crate) fn from_aidl_files(
1195 files: &[(&str, &str)],
1196 main: &str,
1197 module: &str,
1198 enabled_async: bool,
1199 ) -> String {
1200 let dir = TempDir::new(&format!("multi_{module}"));
1201 for (rel, body) in files {
1202 let path = dir.path().join(rel);
1203 std::fs::create_dir_all(path.parent().expect("relative path")).expect("mkdir");
1204 std::fs::write(&path, body).expect("write aidl");
1205 }
1206 rsbinder_aidl::Builder::new()
1207 .source(dir.path().join(main))
1208 .include_dir(dir.path())
1209 .dest_dir(dir.path())
1210 .output(PathBuf::from("golden_multi.rs"))
1211 .set_async_support(enabled_async)
1212 .generate()
1213 .expect("aidl generate");
1214 let text =
1215 std::fs::read_to_string(dir.path().join("golden_multi.rs")).expect("read generated");
1216 extract_module(&text, module)
1217 }
1218
1219 #[test]
1220 fn scalars_and_strings() {
1221 assert_same(
1222 r#"
1223interface IGolden1 {
1224 String echo(in String msg);
1225 int add(in int a, in int b);
1226 boolean flag(in boolean b);
1227}
1228"#,
1229 "IGolden1",
1230 quote! {
1231 pub trait IGolden1 {
1232 fn echo(&self, msg: &str) -> BinderResult<String>;
1233 fn add(&self, a: i32, b: i32) -> BinderResult<i32>;
1234 fn flag(&self, b: bool) -> BinderResult<bool>;
1235 }
1236 },
1237 );
1238 }
1239
1240 #[test]
1241 fn oneway_and_void() {
1242 assert_same(
1243 r#"
1244interface IGolden2 {
1245 oneway void ping();
1246 void nudge(in long n);
1247}
1248"#,
1249 "IGolden2",
1250 quote! {
1251 pub trait IGolden2 {
1252 #[oneway]
1253 fn ping(&self) -> BinderResult<()>;
1254 fn nudge(&self, n: i64) -> BinderResult<()>;
1255 }
1256 },
1257 );
1258 }
1259
1260 #[test]
1261 fn out_and_inout_vectors() {
1262 assert_same(
1263 r#"
1264interface IGolden3 {
1265 void fill(out int[] values);
1266 void bump(inout int[] values);
1267 void take(in int[] values);
1268}
1269"#,
1270 "IGolden3",
1271 quote! {
1272 pub trait IGolden3 {
1273 fn fill(&self, values: &mut Vec<i32>) -> BinderResult<()>;
1274 fn bump(&self, #[inout] values: &mut Vec<i32>) -> BinderResult<()>;
1275 fn take(&self, values: &[i32]) -> BinderResult<()>;
1276 }
1277 },
1278 );
1279 }
1280
1281 #[test]
1283 fn an_out_vec_is_the_array_form_not_the_list_form() {
1284 assert_same(
1285 r#"
1286interface IGolden12 {
1287 void fill(out String[] values);
1288 void maybe(out @nullable String[] values);
1289}
1290"#,
1291 "IGolden12",
1292 quote! {
1293 pub trait IGolden12 {
1294 fn fill(&self, values: &mut Vec<String>) -> BinderResult<()>;
1295 fn maybe(&self, values: &mut Option<Vec<Option<String>>>) -> BinderResult<()>;
1296 }
1297 },
1298 );
1299 }
1300
1301 #[test]
1302 fn nullable_arguments_and_returns() {
1303 assert_same(
1304 r#"
1305interface IGolden4 {
1306 @nullable String maybe(in @nullable String msg, in @nullable byte[] blob);
1307 @nullable String[] some();
1308 String[] every();
1309}
1310"#,
1311 "IGolden4",
1312 quote! {
1313 pub trait IGolden4 {
1314 fn maybe(
1315 &self,
1316 msg: Option<&str>,
1317 blob: Option<&[u8]>,
1318 ) -> BinderResult<Option<String>>;
1319 fn some(&self) -> BinderResult<Option<Vec<Option<String>>>>;
1320 fn every(&self) -> BinderResult<Vec<String>>;
1321 }
1322 },
1323 );
1324 }
1325
1326 #[test]
1327 fn binder_objects() {
1328 assert_same_files(
1330 &[
1331 (
1332 "com/example/IGolden5.aidl",
1333 "package com.example;\nimport com.example.IGolden5Cb;\n\
1334 interface IGolden5 {\n void register(in IGolden5Cb cb);\n\
1335 IGolden5Cb fetch();\n}\n",
1336 ),
1337 (
1338 "com/example/IGolden5Cb.aidl",
1339 "package com.example;\ninterface IGolden5Cb {\n void hit();\n}\n",
1340 ),
1341 ],
1342 "com/example/IGolden5.aidl",
1343 "IGolden5",
1344 "com.example.IGolden5",
1345 quote! {
1346 pub trait IGolden5 {
1347 fn register(
1348 &self,
1349 cb: &rsbinder::Strong<dyn super::IGolden5Cb::IGolden5Cb>,
1350 ) -> BinderResult<()>;
1351 fn fetch(
1352 &self,
1353 ) -> BinderResult<rsbinder::Strong<dyn super::IGolden5Cb::IGolden5Cb>>;
1354 }
1355 },
1356 );
1357 }
1358
1359 #[test]
1361 fn out_fd_arrays_keep_the_null_guard_at_every_arity() {
1362 assert_same(
1363 r#"
1364interface IGolden8 {
1365 void variable(out ParcelFileDescriptor[] fds);
1366 void fixed(out ParcelFileDescriptor[3] fds);
1367 void nested(out ParcelFileDescriptor[2][3] fds);
1368}
1369"#,
1370 "IGolden8",
1371 quote! {
1372 pub trait IGolden8 {
1373 fn variable(
1374 &self,
1375 fds: &mut Vec<Option<rsbinder::ParcelFileDescriptor>>,
1376 ) -> BinderResult<()>;
1377 fn fixed(
1378 &self,
1379 fds: &mut [Option<rsbinder::ParcelFileDescriptor>; 3],
1380 ) -> BinderResult<()>;
1381 fn nested(
1382 &self,
1383 fds: &mut [[Option<rsbinder::ParcelFileDescriptor>; 3]; 2],
1384 ) -> BinderResult<()>;
1385 }
1386 },
1387 );
1388 }
1389
1390 #[test]
1392 fn every_out_and_inout_shape_matches_aidl() {
1393 assert_same_files(
1394 &[
1395 (
1396 "p/IGolden10.aidl",
1397 "package p;\nimport p.IGolden10Cb;\nimport p.Golden10Cfg;\n\
1398 interface IGolden10 {\n\
1399 \x20 void d(out Golden10Cfg v);\n\
1400 \x20 void e(out int[] v);\n\
1401 \x20 void f(out String[] v);\n\
1402 \x20 void g(out ParcelFileDescriptor[] v);\n\
1403 \x20 void h(out IGolden10Cb[] v);\n\
1404 \x20 void i(out Golden10Cfg[] v);\n\
1405 \x20 void j(out int[3] v);\n\
1406 \x20 void k(out ParcelFileDescriptor[3] v);\n\
1407 \x20 void l(out ParcelFileDescriptor[2][3] v);\n\
1408 \x20 void m(out Golden10Cfg[3] v);\n\
1409 \x20 void n(out @nullable Golden10Cfg v);\n\
1410 \x20 void o(out @nullable int[] v);\n\
1411 \x20 void p(out @nullable String[] v);\n\
1412 \x20 void q(out @nullable ParcelFileDescriptor[] v);\n\
1413 \x20 void r(out @nullable int[3] v);\n\
1414 \x20 void s(out @nullable Golden10Cfg[3] v);\n\
1415 \x20 void t(inout ParcelFileDescriptor v);\n\
1416 \x20 void u(inout @nullable ParcelFileDescriptor v);\n\
1417 \x20 void w(inout ParcelFileDescriptor[] v);\n\
1418 \x20 void x(inout IGolden10Cb[] v);\n\
1419 \x20 void y(inout @nullable Golden10Cfg v);\n\
1420 \x20 void z(inout @nullable ParcelFileDescriptor[] v);\n\
1421 \x20 void aa(inout ParcelFileDescriptor[3] v);\n\
1422 \x20 void ab(inout Golden10Cfg[3] v);\n\
1423 \x20 void ac(inout IGolden10Cb[3] v);\n\
1424 \x20 void ad(inout @nullable ParcelFileDescriptor[3] v);\n}\n",
1425 ),
1426 (
1427 "p/IGolden10Cb.aidl",
1428 "package p;\ninterface IGolden10Cb {\n void hit();\n}\n",
1429 ),
1430 (
1431 "p/Golden10Cfg.aidl",
1432 "package p;\nparcelable Golden10Cfg {\n int a;\n}\n",
1433 ),
1434 ],
1435 "p/IGolden10.aidl",
1436 "IGolden10",
1437 "p.IGolden10",
1438 quote! {
1439 pub trait IGolden10 {
1440 fn d(&self, v: &mut super::Golden10Cfg::Golden10Cfg) -> BinderResult<()>;
1441 fn e(&self, v: &mut Vec<i32>) -> BinderResult<()>;
1442 fn f(&self, v: &mut Vec<String>) -> BinderResult<()>;
1443 fn g(
1444 &self,
1445 v: &mut Vec<Option<rsbinder::ParcelFileDescriptor>>,
1446 ) -> BinderResult<()>;
1447 fn h(
1448 &self,
1449 v: &mut Vec<Option<rsbinder::Strong<dyn super::IGolden10Cb::IGolden10Cb>>>,
1450 ) -> BinderResult<()>;
1451 fn i(&self, v: &mut Vec<super::Golden10Cfg::Golden10Cfg>) -> BinderResult<()>;
1452 fn j(&self, v: &mut [i32; 3]) -> BinderResult<()>;
1453 fn k(
1454 &self,
1455 v: &mut [Option<rsbinder::ParcelFileDescriptor>; 3],
1456 ) -> BinderResult<()>;
1457 fn l(
1458 &self,
1459 v: &mut [[Option<rsbinder::ParcelFileDescriptor>; 3]; 2],
1460 ) -> BinderResult<()>;
1461 fn m(&self, v: &mut [super::Golden10Cfg::Golden10Cfg; 3]) -> BinderResult<()>;
1462 fn n(
1463 &self,
1464 v: &mut Option<super::Golden10Cfg::Golden10Cfg>,
1465 ) -> BinderResult<()>;
1466 fn o(&self, v: &mut Option<Vec<i32>>) -> BinderResult<()>;
1467 fn p(&self, v: &mut Option<Vec<Option<String>>>) -> BinderResult<()>;
1468 fn q(
1469 &self,
1470 v: &mut Option<Vec<Option<rsbinder::ParcelFileDescriptor>>>,
1471 ) -> BinderResult<()>;
1472 fn r(&self, v: &mut Option<[i32; 3]>) -> BinderResult<()>;
1473 fn s(
1474 &self,
1475 v: &mut Option<[Option<super::Golden10Cfg::Golden10Cfg>; 3]>,
1476 ) -> BinderResult<()>;
1477 fn t(
1478 &self,
1479 #[inout] v: &mut rsbinder::ParcelFileDescriptor,
1480 ) -> BinderResult<()>;
1481 fn u(
1482 &self,
1483 #[inout] v: &mut Option<rsbinder::ParcelFileDescriptor>,
1484 ) -> BinderResult<()>;
1485 fn w(
1486 &self,
1487 #[inout] v: &mut Vec<rsbinder::ParcelFileDescriptor>,
1488 ) -> BinderResult<()>;
1489 fn x(
1490 &self,
1491 #[inout] v: &mut Vec<rsbinder::Strong<dyn super::IGolden10Cb::IGolden10Cb>>,
1492 ) -> BinderResult<()>;
1493 fn y(
1494 &self,
1495 #[inout] v: &mut Option<super::Golden10Cfg::Golden10Cfg>,
1496 ) -> BinderResult<()>;
1497 fn z(
1498 &self,
1499 #[inout] v: &mut Option<Vec<Option<rsbinder::ParcelFileDescriptor>>>,
1500 ) -> BinderResult<()>;
1501 fn aa(
1502 &self,
1503 #[inout] v: &mut [rsbinder::ParcelFileDescriptor; 3],
1504 ) -> BinderResult<()>;
1505 fn ab(
1506 &self,
1507 #[inout] v: &mut [super::Golden10Cfg::Golden10Cfg; 3],
1508 ) -> BinderResult<()>;
1509 fn ac(
1510 &self,
1511 #[inout] v: &mut [rsbinder::Strong<dyn super::IGolden10Cb::IGolden10Cb>; 3],
1512 ) -> BinderResult<()>;
1513 fn ad(
1514 &self,
1515 #[inout] v: &mut Option<[Option<rsbinder::ParcelFileDescriptor>; 3]>,
1516 ) -> BinderResult<()>;
1517 }
1518 },
1519 );
1520 }
1521
1522 #[test]
1524 fn deprecation_matches_aidl() {
1525 assert_same(
1526 r#"
1527/**
1528 * @deprecated use IGolden12Next
1529 */
1530interface IGolden12 {
1531 /**
1532 * @deprecated use b
1533 */
1534 void a();
1535 void b();
1536}
1537"#,
1538 "IGolden12",
1539 quote! {
1540 #[deprecated = "use IGolden12Next"]
1541 pub trait IGolden12 {
1542 #[deprecated = "use b"]
1543 fn a(&self) -> BinderResult<()>;
1544 fn b(&self) -> BinderResult<()>;
1545 }
1546 },
1547 );
1548 }
1549
1550 #[test]
1552 fn rejects_a_deprecation_the_wire_cannot_carry() {
1553 let err = reject(quote! {
1554 #[deprecated(since = "1.0", note = "use b")]
1555 pub trait IBad {
1556 fn go(&self) -> BinderResult<()>;
1557 }
1558 });
1559 assert!(err.contains("no AIDL form"), "{err}");
1560 }
1561
1562 #[test]
1564 fn rejects_a_repeated_deprecation() {
1565 let err = reject(quote! {
1566 pub trait IBad {
1567 #[deprecated = "a"]
1568 #[deprecated = "b"]
1569 fn go(&self) -> BinderResult<()>;
1570 }
1571 });
1572 assert!(err.contains("more than once"), "{err}");
1573 }
1574
1575 #[test]
1577 fn rejects_a_doc_attribute_the_re_render_would_drop() {
1578 for attr in [quote!(#[doc(hidden)]), quote!(#[doc(alias = "x")])] {
1579 let err = reject(quote! {
1580 #attr
1581 pub trait IBad {
1582 fn go(&self) -> BinderResult<()>;
1583 }
1584 });
1585 assert!(err.contains("unsupported attribute"), "{attr}: {err}");
1586 }
1587 render(quote! {
1589 pub trait IOk {
1591 fn go(&self) -> BinderResult<()>;
1593 }
1594 });
1595 }
1596
1597 #[test]
1599 fn a_nullable_by_value_path_is_pointed_at_the_borrowed_form() {
1600 let err = reject(quote! {
1601 pub trait IBad {
1602 fn go(&self, c: Option<Cfg>) -> BinderResult<()>;
1603 }
1604 });
1605 assert!(err.contains("`Option<&Cfg>`"), "{err}");
1606 assert!(!err.contains("`Cfg` or"), "{err}");
1607 }
1608
1609 #[test]
1611 fn every_in_array_shape_matches_aidl() {
1612 assert_same_files(
1613 &[
1614 (
1615 "p/IGolden14.aidl",
1616 "package p;\nimport p.IGolden14Cb;\nimport p.Golden14Cfg;\n\
1617 interface IGolden14 {\n\
1618 \x20 void a(in int[] v);\n\
1619 \x20 void b(in @nullable int[] v);\n\
1620 \x20 void c(in String[] v);\n\
1621 \x20 void d(in @nullable String[] v);\n\
1622 \x20 void e(in Golden14Cfg[] v);\n\
1623 \x20 void f(in @nullable Golden14Cfg[] v);\n\
1624 \x20 void g(in ParcelFileDescriptor[] v);\n\
1625 \x20 void h(in @nullable ParcelFileDescriptor[] v);\n\
1626 \x20 void i(in IGolden14Cb[] v);\n\
1627 \x20 void j(in @nullable IGolden14Cb[] v);\n\
1628 \x20 void k(in IBinder[] v);\n\
1629 \x20 void l(in @nullable IBinder[] v);\n\
1630 \x20 void m(in Golden14Cfg[3] v);\n\
1631 \x20 void n(in @nullable Golden14Cfg[3] v);\n\
1632 \x20 void o(in ParcelFileDescriptor[3] v);\n\
1633 \x20 void p(in @nullable ParcelFileDescriptor[3] v);\n}\n",
1634 ),
1635 (
1636 "p/IGolden14Cb.aidl",
1637 "package p;\ninterface IGolden14Cb {\n void hit();\n}\n",
1638 ),
1639 (
1640 "p/Golden14Cfg.aidl",
1641 "package p;\nparcelable Golden14Cfg {\n int a;\n}\n",
1642 ),
1643 ],
1644 "p/IGolden14.aidl",
1645 "IGolden14",
1646 "p.IGolden14",
1647 quote! {
1648 pub trait IGolden14 {
1649 fn a(&self, v: &[i32]) -> BinderResult<()>;
1650 fn b(&self, v: Option<&[i32]>) -> BinderResult<()>;
1651 fn c(&self, v: &[String]) -> BinderResult<()>;
1652 fn d(&self, v: Option<&[Option<String>]>) -> BinderResult<()>;
1653 fn e(&self, v: &[super::Golden14Cfg::Golden14Cfg]) -> BinderResult<()>;
1654 fn f(
1655 &self,
1656 v: Option<&[Option<super::Golden14Cfg::Golden14Cfg>]>,
1657 ) -> BinderResult<()>;
1658 fn g(&self, v: &[rsbinder::ParcelFileDescriptor]) -> BinderResult<()>;
1659 fn h(
1660 &self,
1661 v: Option<&[Option<rsbinder::ParcelFileDescriptor>]>,
1662 ) -> BinderResult<()>;
1663 fn i(
1664 &self,
1665 v: &[rsbinder::Strong<dyn super::IGolden14Cb::IGolden14Cb>],
1666 ) -> BinderResult<()>;
1667 fn j(
1668 &self,
1669 v: Option<&[Option<rsbinder::Strong<dyn super::IGolden14Cb::IGolden14Cb>>]>,
1670 ) -> BinderResult<()>;
1671 fn k(&self, v: &[rsbinder::SIBinder]) -> BinderResult<()>;
1672 fn l(&self, v: Option<&[Option<rsbinder::SIBinder>]>) -> BinderResult<()>;
1673 fn m(&self, v: &[super::Golden14Cfg::Golden14Cfg; 3]) -> BinderResult<()>;
1674 fn n(
1675 &self,
1676 v: Option<&[Option<super::Golden14Cfg::Golden14Cfg>; 3]>,
1677 ) -> BinderResult<()>;
1678 fn o(&self, v: &[rsbinder::ParcelFileDescriptor; 3]) -> BinderResult<()>;
1679 fn p(
1680 &self,
1681 v: Option<&[Option<rsbinder::ParcelFileDescriptor>; 3]>,
1682 ) -> BinderResult<()>;
1683 }
1684 },
1685 );
1686 }
1687
1688 #[test]
1690 fn rejects_an_array_of_options_aidl_would_spell_bare() {
1691 for decl in [
1692 quote!(
1694 fn go(&self, v: &[Option<rsbinder::ParcelFileDescriptor>]) -> BinderResult<()>;
1695 ),
1696 quote!(
1698 fn go(&self, v: &mut Vec<Option<String>>) -> BinderResult<()>;
1699 ),
1700 quote!(
1702 fn go(
1703 &self,
1704 #[inout] v: &mut Vec<Option<rsbinder::ParcelFileDescriptor>>,
1705 ) -> BinderResult<()>;
1706 ),
1707 ] {
1708 let err = reject(quote! {
1709 pub trait IBad {
1710 #decl
1711 }
1712 });
1713 assert!(err.contains("`Option<_>` elements"), "{err}");
1714 }
1715 }
1716
1717 #[test]
1719 fn rejects_array_elements_aidl_spells_the_other_way() {
1720 for (decl, needle) in [
1721 (
1722 quote!(
1723 fn go(
1724 &self,
1725 #[inout] v: &mut [Option<rsbinder::ParcelFileDescriptor>; 3],
1726 ) -> BinderResult<()>;
1727 ),
1728 "an `inout` array cannot have `Option<_>` elements",
1729 ),
1730 (
1731 quote!(
1732 fn go(
1733 &self,
1734 #[inout] v: &mut [Option<rsbinder::Strong<dyn IOther>>; 3],
1735 ) -> BinderResult<()>;
1736 ),
1737 "an `inout` array cannot have `Option<_>` elements",
1738 ),
1739 (
1740 quote!(
1741 fn go(&self, v: Option<&[String]>) -> BinderResult<()>;
1742 ),
1743 "a nullable `in` array",
1744 ),
1745 (
1746 quote!(
1747 fn go(&self, v: Option<&[rsbinder::ParcelFileDescriptor]>) -> BinderResult<()>;
1748 ),
1749 "a nullable `in` array",
1750 ),
1751 (
1752 quote!(
1753 fn go(&self, v: &mut Option<Vec<String>>) -> BinderResult<()>;
1754 ),
1755 "a nullable `out` array",
1756 ),
1757 (
1758 quote!(
1759 fn go(
1760 &self,
1761 v: &mut Option<[rsbinder::ParcelFileDescriptor; 3]>,
1762 ) -> BinderResult<()>;
1763 ),
1764 "a nullable `out` array",
1765 ),
1766 (
1768 quote!(
1769 fn go(
1770 &self,
1771 v: Option<&[rsbinder::ParcelFileDescriptor; 3]>,
1772 ) -> BinderResult<()>;
1773 ),
1774 "a nullable `in` array",
1775 ),
1776 (
1778 quote!(
1779 fn go(&self) -> BinderResult<Option<Vec<String>>>;
1780 ),
1781 "a nullable returned array",
1782 ),
1783 (
1784 quote!(
1785 fn go(&self) -> BinderResult<Option<Vec<rsbinder::Strong<dyn IOther>>>>;
1786 ),
1787 "a nullable returned array",
1788 ),
1789 (
1790 quote!(
1791 fn go(&self) -> BinderResult<Vec<Option<String>>>;
1792 ),
1793 "a returned array cannot have",
1794 ),
1795 (
1796 quote!(
1797 fn go(&self) -> BinderResult<Option<[String; 3]>>;
1798 ),
1799 "a nullable returned array",
1800 ),
1801 ] {
1802 let err = reject(quote! {
1803 pub trait IBad {
1804 #decl
1805 }
1806 });
1807 assert!(err.contains(needle), "{needle}: {err}");
1808 }
1809 render(quote! {
1811 pub trait IOk {
1812 fn a(
1813 &self,
1814 #[inout] v: &mut [rsbinder::ParcelFileDescriptor; 3],
1815 ) -> BinderResult<()>;
1816 fn a2(&self, #[inout] v: &mut [rsbinder::Strong<dyn IOther>; 3]) -> BinderResult<()>;
1817 fn b(&self, v: Option<&[Option<String>]>) -> BinderResult<()>;
1818 fn c(
1819 &self,
1820 v: Option<&[Option<rsbinder::ParcelFileDescriptor>; 3]>,
1821 ) -> BinderResult<()>;
1822 fn d(&self) -> BinderResult<Option<Vec<Option<String>>>>;
1823 fn e(&self) -> BinderResult<Vec<String>>;
1824 fn f(&self) -> BinderResult<Option<[Option<String>; 3]>>;
1825 }
1826 });
1827 }
1828
1829 #[test]
1831 fn rejects_arrays_nested_the_way_aidl_cannot_render() {
1832 for (decl, needle) in [
1833 (
1834 quote!(
1835 fn go(&self) -> BinderResult<Vec<[i32; 3]>>;
1836 ),
1837 "fixed-size in every dimension",
1838 ),
1839 (
1840 quote!(
1841 fn go(&self, v: &[[i32; 3]]) -> BinderResult<()>;
1842 ),
1843 "fixed-size in every dimension",
1844 ),
1845 (
1846 quote!(
1847 fn go(
1848 &self,
1849 v: &mut Vec<[rsbinder::ParcelFileDescriptor; 3]>,
1850 ) -> BinderResult<()>;
1851 ),
1852 "fixed-size in every dimension",
1853 ),
1854 (
1855 quote!(
1856 fn go(&self) -> BinderResult<Vec<Vec<i32>>>;
1857 ),
1858 "fixed-size in every dimension",
1859 ),
1860 (
1861 quote!(
1862 fn go(&self) -> BinderResult<Option<Vec<Option<[String; 3]>>>>;
1863 ),
1864 "`Option` of another array",
1865 ),
1866 (
1867 quote!(
1868 fn go(&self) -> BinderResult<[Option<[String; 3]>; 2]>;
1869 ),
1870 "`Option` of another array",
1871 ),
1872 ] {
1873 let err = reject(quote! {
1874 pub trait IBad {
1875 #decl
1876 }
1877 });
1878 assert!(err.contains(needle), "{needle}: {err}");
1879 }
1880 }
1881
1882 #[test]
1884 fn rejects_a_borrowed_owned_container() {
1885 for (ty, needle) in [
1886 (quote!(&String), "&str"),
1887 (quote!(&Vec<i32>), "&[T]"),
1888 (quote!(Option<&String>), "&str"),
1889 (quote!(Option<&Vec<i32>>), "&[T]"),
1890 (quote!(&Option<String>), "Option<&T>"),
1891 (quote!(Option<&Option<String>>), "Option<&T>"),
1892 (quote!(&i32), "drop the `&`"),
1893 (quote!(&bool), "drop the `&`"),
1894 ] {
1895 let err = reject(quote! {
1896 pub trait IBad {
1897 fn go(&self, v: #ty) -> BinderResult<()>;
1898 }
1899 });
1900 assert!(err.contains(needle), "{ty}: {err}");
1901 }
1902 render(quote! {
1904 pub trait IOk {
1905 fn go(&self, v: &mut Vec<i32>) -> BinderResult<()>;
1906 }
1907 });
1908 }
1909
1910 #[test]
1912 fn rejects_return_types_with_no_wire_form() {
1913 for (ty, needle) in [
1914 (quote!(dyn std::fmt::Debug), "trait object"),
1915 (quote!(<Vec<i32> as IntoIterator>::Item), "qualified path"),
1916 ] {
1917 let err = reject(quote! {
1918 pub trait IBad {
1919 fn go(&self) -> BinderResult<#ty>;
1920 }
1921 });
1922 assert!(err.contains(needle), "{ty}: {err}");
1923 }
1924 }
1925
1926 #[test]
1928 fn rejects_scalars_aidl_never_renders() {
1929 for (decl, needle) in [
1930 (
1931 quote!(
1932 fn go(&self, n: u32) -> BinderResult<()>;
1933 ),
1934 "`u32`",
1935 ),
1936 (
1937 quote!(
1938 fn go(&self, n: i16) -> BinderResult<()>;
1939 ),
1940 "`i16`",
1941 ),
1942 (
1943 quote!(
1944 fn go(&self, n: u8) -> BinderResult<()>;
1945 ),
1946 "`u8`",
1947 ),
1948 (
1949 quote!(
1950 fn go(&self, n: char) -> BinderResult<()>;
1951 ),
1952 "`char`",
1953 ),
1954 (
1955 quote!(
1956 fn go(&self, v: &[u32]) -> BinderResult<()>;
1957 ),
1958 "`u32`",
1959 ),
1960 (
1962 quote!(
1963 fn go(&self, v: &[i8]) -> BinderResult<()>;
1964 ),
1965 "element spelling",
1966 ),
1967 (
1968 quote!(
1969 fn go(&self) -> BinderResult<u64>;
1970 ),
1971 "`u64`",
1972 ),
1973 (
1974 quote!(
1975 fn go(&self) -> BinderResult<u128>;
1976 ),
1977 "`u128`",
1978 ),
1979 (
1981 quote!(
1982 fn go(&self) -> BinderResult<Box<u128>>;
1983 ),
1984 "`u128`",
1985 ),
1986 (
1987 quote!(
1988 fn go(&self, p: &Pair<u32>) -> BinderResult<()>;
1989 ),
1990 "`u32`",
1991 ),
1992 ] {
1993 let err = reject(quote! {
1994 pub trait IBad {
1995 #decl
1996 }
1997 });
1998 assert!(err.contains(needle), "{needle}: {err}");
1999 }
2000 render(quote! {
2002 pub trait IOk {
2003 fn a(&self, v: bool, w: i8, x: i32, y: i64) -> BinderResult<()>;
2004 fn b(&self, v: f32, w: f64, x: u16) -> BinderResult<()>;
2005 fn c(&self, v: &[u8]) -> BinderResult<()>;
2006 fn d(&self, v: Option<&[u8]>) -> BinderResult<()>;
2007 fn e(&self) -> BinderResult<i64>;
2008 }
2009 });
2010 }
2011
2012 #[test]
2014 fn rejects_a_box_aidl_never_renders() {
2015 for decl in [
2016 quote!(
2017 fn go(&self) -> BinderResult<Box<Cfg>>;
2018 ),
2019 quote!(
2020 fn go(&self, c: &Box<Cfg>) -> BinderResult<()>;
2021 ),
2022 quote!(
2023 fn go(&self, c: &mut Box<Cfg>) -> BinderResult<()>;
2024 ),
2025 quote!(
2026 fn go(&self) -> BinderResult<Vec<Option<Box<Cfg>>>>;
2027 ),
2028 quote!(
2029 fn go(&self) -> BinderResult<Option<Box<i32>>>;
2030 ),
2031 quote!(
2032 fn go(&self, c: Option<Box<Cfg>>) -> BinderResult<()>;
2033 ),
2034 quote!(
2035 fn go(&self) -> BinderResult<std::boxed::Box<Cfg>>;
2036 ),
2037 quote!(
2038 fn go(&self) -> BinderResult<Option<Box<Cfg>>>;
2039 ),
2040 quote!(
2041 fn go(&self, c: Option<&Box<Cfg>>) -> BinderResult<()>;
2042 ),
2043 quote!(
2044 fn go(&self, c: &mut Option<Box<Cfg>>) -> BinderResult<()>;
2045 ),
2046 ] {
2047 let err = reject(quote! {
2048 pub trait IBad {
2049 #decl
2050 }
2051 });
2052 assert!(err.contains("never renders this `Box`"), "{decl}: {err}");
2053 }
2054 render(quote! {
2055 pub trait IOk {
2056 fn d(&self) -> BinderResult<crate::p::Box<Cfg>>;
2057 }
2058 });
2059 let input: syn::DeriveInput = syn::parse2(quote! {
2060 pub struct Node {
2061 pub next: Option<Box<Node>>,
2062 }
2063 })
2064 .unwrap();
2065 parcelable::render_source(&input).expect("a field's `Option<Box<T>>` is accepted");
2066 }
2067
2068 #[test]
2070 fn rejects_a_generic_argument_aidl_never_renders() {
2071 for decl in [
2072 quote!(
2073 fn go(&self) -> BinderResult<Pair<Vec<i32>>>;
2074 ),
2075 quote!(
2076 fn go(&self, p: &Pair<[i32; 3]>) -> BinderResult<()>;
2077 ),
2078 quote!(
2079 fn go(&self) -> BinderResult<Vec<Pair<Vec<i32>>>>;
2080 ),
2081 quote!(
2082 fn go(&self) -> BinderResult<crate::p::Box<Option<Cfg>>>;
2083 ),
2084 ] {
2085 let err = reject(quote! {
2086 pub trait IBad {
2087 #decl
2088 }
2089 });
2090 assert!(err.contains("names a type argument bare"), "{decl}: {err}");
2091 }
2092 let input: syn::DeriveInput = syn::parse2(quote! {
2093 pub struct Holder {
2094 pub p: Pair<Option<Cfg>>,
2095 }
2096 })
2097 .unwrap();
2098 let err = parcelable::render_source(&input)
2099 .expect_err("a field's `Pair<Option<Cfg>>` is refused")
2100 .to_string();
2101 assert!(err.contains("names a type argument bare"), "{err}");
2102 render(quote! {
2103 pub trait IOk {
2104 fn a(&self, p: &Pair<Cfg>) -> BinderResult<Option<Pair<Pair<String>>>>;
2105 fn b(&self) -> BinderResult<Vec<Pair<i32>>>;
2106 }
2107 });
2108 }
2109
2110 #[test]
2112 fn rejects_an_empty_descriptor() {
2113 let err = match syn::parse2::<Args>(quote!(descriptor = "")) {
2114 Ok(_) => panic!("an empty `descriptor` must be refused"),
2115 Err(e) => e.to_string(),
2116 };
2117 assert!(err.contains("cannot be empty"), "{err}");
2118 }
2119
2120 #[test]
2122 fn self_referencing_interface() {
2123 assert_same(
2124 r#"
2125interface IGolden6 {
2126 void register(in IGolden6 cb);
2127 IGolden6 fetch();
2128}
2129"#,
2130 "IGolden6",
2131 quote! {
2132 pub trait IGolden6 {
2133 fn register(
2134 &self,
2135 cb: &rsbinder::Strong<dyn IGolden6>,
2136 ) -> BinderResult<()>;
2137 fn fetch(&self) -> BinderResult<rsbinder::Strong<dyn IGolden6>>;
2138 }
2139 },
2140 );
2141 }
2142
2143 #[test]
2145 fn parcelable_codec_matches_aidl() {
2146 let input: syn::DeriveInput = syn::parse2(quote! {
2147 pub struct GoldenConfig {
2148 pub name: String,
2149 pub retries: i32,
2150 pub verbose: bool,
2151 pub timeoutNanos: i64,
2152 pub ratio: f64,
2153 pub extra: Option<Vec<u8>>,
2154 pub tags: Vec<String>,
2155 pub labels: Option<Vec<Option<String>>>,
2156 pub slots: [String; 3],
2157 pub fds: [Option<rsbinder::ParcelFileDescriptor>; 3],
2158 pub ibinder: Option<rsbinder::SIBinder>,
2159 }
2160 })
2161 .unwrap();
2162 let actual = parcelable::render_source(&input).expect("render");
2163 for enabled_async in [false, true] {
2164 let expected = from_aidl(
2165 r#"
2166parcelable GoldenConfig {
2167 String name;
2168 int retries;
2169 boolean verbose;
2170 long timeoutNanos;
2171 double ratio;
2172 @nullable byte[] extra;
2173 String[] tags;
2174 @nullable String[] labels;
2175 String[3] slots;
2176 ParcelFileDescriptor[3] fds;
2177 @nullable IBinder ibinder;
2178}
2179"#,
2180 "GoldenConfig",
2181 enabled_async,
2182 );
2183 assert_eq!(expected, actual, "generated parcelable differs");
2184 }
2185 }
2186
2187 #[test]
2189 fn parcelable_and_enum_arguments() {
2190 let files = [
2191 (
2192 "com/example/IGolden7.aidl",
2193 "package com.example;\nimport com.example.GoldenCfg;\n\
2194 import com.example.GoldenMode;\n\
2195 interface IGolden7 {\n\
2196 \x20 GoldenCfg apply(in GoldenCfg cfg, in GoldenMode mode);\n}\n",
2197 ),
2198 (
2199 "com/example/GoldenCfg.aidl",
2200 "package com.example;\nparcelable GoldenCfg {\n String name;\n\
2201 \x20 int retries;\n}\n",
2202 ),
2203 (
2204 "com/example/GoldenMode.aidl",
2205 "package com.example;\n@Backing(type=\"int\")\n\
2206 enum GoldenMode {\n FAST = 0,\n SAFE = 1,\n}\n",
2207 ),
2208 ];
2209 assert_same_files(
2210 &files,
2211 "com/example/IGolden7.aidl",
2212 "IGolden7",
2213 "com.example.IGolden7",
2214 quote! {
2215 pub trait IGolden7 {
2216 fn apply(
2217 &self,
2218 cfg: &super::GoldenCfg::GoldenCfg,
2219 mode: super::GoldenMode::GoldenMode,
2220 ) -> BinderResult<super::GoldenCfg::GoldenCfg>;
2221 }
2222 },
2223 );
2224 }
2225
2226 #[test]
2227 fn rejects_generic_trait() {
2228 let item: ItemTrait = syn::parse2(quote! {
2229 pub trait IBad<T> {
2230 fn go(&self) -> BinderResult<()>;
2231 }
2232 })
2233 .unwrap();
2234 let err = render_source(&Args { descriptor: None }, &item).unwrap_err();
2235 assert!(err.to_string().contains("cannot be generic"), "{err}");
2236 }
2237
2238 #[test]
2239 fn rejects_oneway_with_return() {
2240 let item: ItemTrait = syn::parse2(quote! {
2241 pub trait IBad {
2242 #[oneway]
2243 fn go(&self) -> BinderResult<i32>;
2244 }
2245 })
2246 .unwrap();
2247 let err = render_source(&Args { descriptor: None }, &item).unwrap_err();
2248 assert!(err.to_string().contains("cannot return a value"), "{err}");
2249 }
2250
2251 #[test]
2252 fn rejects_missing_receiver() {
2253 let item: ItemTrait = syn::parse2(quote! {
2254 pub trait IBad {
2255 fn go() -> BinderResult<()>;
2256 }
2257 })
2258 .unwrap();
2259 let err = render_source(&Args { descriptor: None }, &item).unwrap_err();
2260 assert!(err.to_string().contains("&self"), "{err}");
2261 }
2262
2263 #[test]
2264 fn descriptor_defaults_to_trait_name_and_can_be_overridden() {
2265 let item: ItemTrait = syn::parse2(quote! {
2266 pub trait IHello {
2267 fn go(&self) -> BinderResult<()>;
2268 }
2269 })
2270 .unwrap();
2271 let bare = render_source(&Args { descriptor: None }, &item).unwrap();
2272 assert!(bare.contains(r#""IHello""#), "{bare}");
2273 let named = render_source(
2274 &Args {
2275 descriptor: Some("com.example.IHello".into()),
2276 },
2277 &item,
2278 )
2279 .unwrap();
2280 assert!(named.contains(r#""com.example.IHello""#), "{named}");
2281 }
2282
2283 fn render(tokens: proc_macro2::TokenStream) -> String {
2284 let item: ItemTrait = syn::parse2(tokens).expect("parse trait");
2285 render_source(&Args { descriptor: None }, &item).expect("render")
2286 }
2287
2288 fn reject(tokens: proc_macro2::TokenStream) -> String {
2289 let item: ItemTrait = syn::parse2(tokens).expect("parse trait");
2290 render_source(&Args { descriptor: None }, &item)
2291 .unwrap_err()
2292 .to_string()
2293 }
2294
2295 #[test]
2297 fn raw_identifiers_are_not_double_escaped() {
2298 let s = render(quote! {
2299 pub trait IRaw {
2300 fn r#type(&self, r#match: i32) -> BinderResult<()>;
2301 }
2302 });
2303 assert!(!s.contains("r#r#"), "{s}");
2304 assert!(!s.contains("_arg_r#"), "{s}");
2305 assert!(s.contains("fn r#type("), "{s}");
2306 assert!(s.contains("_arg_match"), "{s}");
2307 syn::parse_file(&s).unwrap_or_else(|e| panic!("does not parse: {e}\n{s}"));
2308 }
2309
2310 #[test]
2312 fn raw_trait_name_renders() {
2313 let item: ItemTrait = syn::parse2(quote! {
2314 pub trait r#type {
2315 fn go(&self) -> BinderResult<()>;
2316 }
2317 })
2318 .unwrap();
2319 let s = render_source(&Args { descriptor: None }, &item).expect("render");
2320 assert!(s.contains("pub trait r#type"), "{s}");
2321 assert!(s.contains("Bntype"), "{s}");
2322 assert!(!s.contains("r#r#"), "{s}");
2323 syn::parse_file(&s).unwrap_or_else(|e| panic!("does not parse: {e}\n{s}"));
2324 }
2325
2326 #[test]
2328 fn rejects_trait_level_attributes() {
2329 let err = reject(quote! {
2330 #[cfg(feature = "x")]
2331 pub trait IBad {
2332 fn go(&self) -> BinderResult<()>;
2333 }
2334 });
2335 assert!(err.contains("unsupported attribute"), "{err}");
2336
2337 let err = reject(quote! {
2338 #[oneway]
2339 pub trait IBad {
2340 fn go(&self) -> BinderResult<()>;
2341 }
2342 });
2343 assert!(err.contains("unsupported attribute"), "{err}");
2344 }
2345
2346 #[test]
2347 fn rejects_where_clauses() {
2348 let err = reject(quote! {
2349 pub trait IBad where Self: Sized {
2350 fn go(&self) -> BinderResult<()>;
2351 }
2352 });
2353 assert!(err.contains("where clause"), "{err}");
2354
2355 let err = reject(quote! {
2356 pub trait IBad {
2357 fn go(&self) -> BinderResult<()> where Self: Sized;
2358 }
2359 });
2360 assert!(err.contains("where clause"), "{err}");
2361 }
2362
2363 #[test]
2365 fn rejects_signature_modifiers() {
2366 let err = reject(quote! {
2367 pub trait IBad {
2368 async fn go(&self) -> BinderResult<()>;
2369 }
2370 });
2371 assert!(err.contains("sync"), "{err}");
2372
2373 let err = reject(quote! {
2374 pub trait IBad {
2375 unsafe fn go(&self) -> BinderResult<()>;
2376 }
2377 });
2378 assert!(err.contains("unsafe"), "{err}");
2379 }
2380
2381 #[test]
2383 fn rejects_non_shared_receivers() {
2384 for recv in [
2385 quote!(self),
2386 quote!(mut self),
2387 quote!(&mut self),
2388 quote!(&'static self),
2389 quote!(self: Box<Self>),
2390 ] {
2391 let err = reject(quote! {
2392 pub trait IBad {
2393 fn go(#recv) -> BinderResult<()>;
2394 }
2395 });
2396 assert!(err.contains("`&self`"), "{recv}: {err}");
2397 }
2398 let err = reject(quote! {
2400 pub trait IBad {
2401 fn go(&'static self) -> BinderResult<()>;
2402 }
2403 });
2404 assert!(err.contains("explicit lifetime on `self`"), "{err}");
2405 render(quote! {
2407 pub trait IOk {
2408 fn go(&'_ self) -> BinderResult<()>;
2409 }
2410 });
2411 }
2412
2413 #[test]
2415 fn rejects_an_attribute_on_the_receiver() {
2416 for recv in [
2417 quote!(
2418 #[oneway]
2419 &self
2420 ),
2421 quote!(
2422 #[cfg(feature = "never")]
2423 &self
2424 ),
2425 ] {
2426 let err = reject(quote! {
2427 pub trait IBad {
2428 fn go(#recv) -> BinderResult<()>;
2429 }
2430 });
2431 assert!(err.contains("unsupported attribute"), "{recv}: {err}");
2432 }
2433 }
2434
2435 #[test]
2437 fn rejects_borrowed_return_inside_option() {
2438 let err = reject(quote! {
2439 pub trait IBad {
2440 fn go(&self) -> BinderResult<Option<&str>>;
2441 }
2442 });
2443 assert!(err.contains("borrow"), "{err}");
2444 }
2445
2446 #[test]
2448 fn rejects_out_nullable_string() {
2449 for decl in [
2450 quote!(
2451 fn go(&self, s: &mut Option<String>) -> BinderResult<()>;
2452 ),
2453 quote!(
2454 fn go(&self, #[inout] s: &mut Option<String>) -> BinderResult<()>;
2455 ),
2456 ] {
2457 let err = reject(quote! {
2458 pub trait IBad {
2459 #decl
2460 }
2461 });
2462 assert!(err.contains("`String` cannot be an"), "{err}");
2463 }
2464 }
2465
2466 #[test]
2468 fn rejects_the_directions_aidl_refuses_for_a_binder_or_fd() {
2469 for decl in [
2470 quote!(
2471 fn go(&self, v: &mut rsbinder::Strong<dyn IOther>) -> BinderResult<()>;
2472 ),
2473 quote!(
2474 fn go(&self, v: &mut Option<rsbinder::Strong<dyn IOther>>) -> BinderResult<()>;
2475 ),
2476 quote!(
2477 fn go(&self, #[inout] v: &mut rsbinder::Strong<dyn IOther>) -> BinderResult<()>;
2478 ),
2479 quote!(
2480 fn go(&self, #[inout] v: &mut Option<rsbinder::SIBinder>) -> BinderResult<()>;
2481 ),
2482 quote!(
2483 fn go(&self, v: &mut rsbinder::SIBinder) -> BinderResult<()>;
2484 ),
2485 quote!(
2486 fn go(&self, v: &mut Option<rsbinder::SIBinder>) -> BinderResult<()>;
2487 ),
2488 quote!(
2489 fn go(&self, v: &mut rsbinder::ParcelFileDescriptor) -> BinderResult<()>;
2490 ),
2491 quote!(
2492 fn go(&self, v: &mut Option<rsbinder::ParcelFileDescriptor>) -> BinderResult<()>;
2493 ),
2494 ] {
2495 let err = reject(quote! {
2496 pub trait IBad {
2497 #decl
2498 }
2499 });
2500 assert!(
2501 err.contains("parameter — `.aidl` passes it"),
2502 "{decl}: {err}"
2503 );
2504 }
2505 let err = reject(quote! {
2507 pub trait IBad {
2508 fn go(&self, #[nonnull] v: &mut Vec<rsbinder::SIBinder>) -> BinderResult<()>;
2509 }
2510 });
2511 assert!(err.contains("unsupported attribute"), "{err}");
2512 render(quote! {
2514 pub trait IOk {
2515 fn a(&self, #[inout] v: &mut rsbinder::ParcelFileDescriptor) -> BinderResult<()>;
2516 fn b(&self, #[inout] v: &mut Option<rsbinder::ParcelFileDescriptor>)
2517 -> BinderResult<()>;
2518 fn c(&self, v: &mut Vec<Option<rsbinder::Strong<dyn IOther>>>) -> BinderResult<()>;
2519 fn d(&self, #[inout] v: &mut Vec<rsbinder::SIBinder>) -> BinderResult<()>;
2520 }
2521 });
2522 }
2523
2524 #[test]
2526 fn rejects_an_out_array_of_bare_binder_objects_or_fds() {
2527 for ty in [
2528 quote!(&mut Vec<rsbinder::ParcelFileDescriptor>),
2529 quote!(&mut Option<Vec<rsbinder::ParcelFileDescriptor>>),
2530 quote!(&mut Vec<rsbinder::Strong<dyn IOther>>),
2531 quote!(&mut [rsbinder::SIBinder; 3]),
2532 quote!(&mut [[rsbinder::ParcelFileDescriptor; 3]; 2]),
2533 ] {
2534 let err = reject(quote! {
2535 pub trait IBad {
2536 fn go(&self, v: #ty) -> BinderResult<()>;
2537 }
2538 });
2539 assert!(err.contains("`Option<_>` elements"), "{ty}: {err}");
2540 }
2541 render(quote! {
2543 pub trait IOk {
2544 fn a(&self, #[inout] v: &mut Vec<rsbinder::ParcelFileDescriptor>)
2545 -> BinderResult<()>;
2546 }
2547 });
2548 }
2549
2550 #[test]
2552 fn a_macro_interpolated_type_renders_like_the_bare_one() {
2553 let str_ty = proc_macro2::Group::new(proc_macro2::Delimiter::None, quote!(str));
2554 let ref_str = proc_macro2::Group::new(proc_macro2::Delimiter::None, quote!(&str));
2555 assert_same(
2556 r#"
2557interface IGolden10 {
2558 void take(in String s, in @nullable String t);
2559}
2560"#,
2561 "IGolden10",
2562 quote! {
2563 pub trait IGolden10 {
2564 fn take(&self, s: &#str_ty, t: Option<#ref_str>) -> BinderResult<()>;
2565 }
2566 },
2567 );
2568 }
2569
2570 #[test]
2572 fn a_macro_interpolated_parameter_keeps_its_direction() {
2573 let vec_ty = proc_macro2::Group::new(proc_macro2::Delimiter::None, quote!(&mut Vec<i32>));
2574 let ret_ty =
2575 proc_macro2::Group::new(proc_macro2::Delimiter::None, quote!(BinderResult<()>));
2576 assert_same(
2577 r#"
2578interface IGolden11 {
2579 void fill(out int[] v);
2580 void bump(inout int[] v);
2581}
2582"#,
2583 "IGolden11",
2584 quote! {
2585 pub trait IGolden11 {
2586 fn fill(&self, v: #vec_ty) -> #ret_ty;
2587 fn bump(&self, #[inout] v: #vec_ty) -> #ret_ty;
2588 }
2589 },
2590 );
2591 }
2592
2593 #[test]
2595 fn rejects_argument_shapes_with_no_wire_form() {
2596 for (ty, needle) in [
2597 (quote!(()), "`()` has no wire form"),
2598 (quote!(dyn IOther), "trait object"),
2599 (quote!(&dyn IOther), "trait object"),
2600 (quote!(Option<&dyn IOther>), "trait object"),
2601 (quote!(&[()]), "`()` has no wire form"),
2602 (quote!(Vec<()>), "`()` has no wire form"),
2603 (quote!(&[dyn IOther]), "trait object"),
2604 (quote!(&mut [i32]), "`&mut [T]`"),
2605 ] {
2606 let err = reject(quote! {
2607 pub trait IBad {
2608 fn go(&self, v: #ty) -> BinderResult<()>;
2609 }
2610 });
2611 assert!(err.contains(needle), "{ty}: {err}");
2612 }
2613 }
2614
2615 #[test]
2617 fn rejects_a_nullable_primitive_behind_a_slice_or_array() {
2618 for ty in [
2619 quote!(&[Option<i32>]),
2620 quote!([Option<i32>; 4]),
2621 quote!(Vec<Option<i32>>),
2623 ] {
2624 let err = reject(quote! {
2625 pub trait IBad {
2626 fn go(&self, v: #ty) -> BinderResult<()>;
2627 }
2628 });
2629 assert!(err.contains("cannot be nullable"), "{ty}: {err}");
2630 }
2631 }
2632
2633 #[test]
2635 fn rejects_a_parcelable_holder_in_a_signature() {
2636 for decl in [
2637 quote!(
2638 fn go(&self, h: &rsbinder::ParcelableHolder) -> BinderResult<()>;
2639 ),
2640 quote!(
2641 fn go(&self, h: &[rsbinder::ParcelableHolder]) -> BinderResult<()>;
2642 ),
2643 quote!(
2644 fn go(&self) -> BinderResult<rsbinder::ParcelableHolder>;
2645 ),
2646 ] {
2647 let err = reject(quote! {
2648 pub trait IBad {
2649 #decl
2650 }
2651 });
2652 assert!(err.contains("`ParcelableHolder` cannot appear"), "{err}");
2653 }
2654 }
2655
2656 #[test]
2658 fn rejects_duplicate_names() {
2659 let err = reject(quote! {
2660 pub trait IBad {
2661 fn go(&self, a: i32, a: i32) -> BinderResult<()>;
2662 }
2663 });
2664 assert!(err.contains("duplicate argument name `a`"), "{err}");
2665
2666 let err = reject(quote! {
2668 pub trait IBad {
2669 fn go(&self, r#type: i32, r#type: i32) -> BinderResult<()>;
2670 }
2671 });
2672 assert!(err.contains("duplicate argument name `type`"), "{err}");
2673
2674 let err = reject(quote! {
2675 pub trait IBad {
2676 fn go(&self) -> BinderResult<()>;
2677 fn go(&self, a: i32) -> BinderResult<()>;
2678 }
2679 });
2680 assert!(err.contains("duplicate method name `go`"), "{err}");
2681 }
2682
2683 #[test]
2685 fn rejects_a_method_named_like_a_generated_trait_item() {
2686 for name in [
2687 "descriptor",
2688 "getDefaultImpl",
2689 "setDefaultImpl",
2690 "dump",
2691 "as_binder",
2692 ] {
2693 let ident = syn::Ident::new(name, proc_macro2::Span::call_site());
2694 let err = reject(quote! {
2695 pub trait IBad {
2696 fn #ident(&self, a: i32) -> BinderResult<()>;
2697 }
2698 });
2699 assert!(
2700 err.contains(&format!(
2701 "method `{name}` collides with an item of the generated Rust trait"
2702 )),
2703 "{err}"
2704 );
2705 }
2706 let err = reject(quote! {
2708 pub trait IBad {
2709 fn r#dump(&self) -> BinderResult<()>;
2710 }
2711 });
2712 assert!(err.contains("method `dump` collides"), "{err}");
2713 }
2714
2715 #[test]
2717 fn rejects_the_aidl_reserved_method_signatures() {
2718 for tokens in [
2719 quote! { pub trait IBad { fn asBinder(&self) -> BinderResult<()>; } },
2720 quote! { pub trait IBad { fn getInterfaceHash(&self) -> BinderResult<String>; } },
2721 quote! { pub trait IBad { fn getInterfaceVersion(&self) -> BinderResult<i32>; } },
2722 quote! { pub trait IBad { fn getTransactionName(&self, c: i32) -> BinderResult<String>; } },
2723 ] {
2724 let err = reject(tokens);
2725 assert!(err.contains("is reserved for internal use"), "{err}");
2726 }
2727 render(quote! {
2729 pub trait IOk {
2730 fn getInterfaceVersion(&self, a: i32) -> BinderResult<i32>;
2731 fn getTransactionName(&self) -> BinderResult<String>;
2732 }
2733 });
2734 }
2735
2736 #[test]
2738 fn an_empty_allow_list_names_no_attribute() {
2739 let err = reject(quote! {
2740 pub trait IBad {
2741 fn go(#[allow(dead_code)] &self) -> BinderResult<()>;
2742 }
2743 });
2744 assert_eq!(
2745 err, "unsupported attribute; no attribute is supported here",
2746 "{err}"
2747 );
2748 }
2749
2750 #[test]
2752 fn rejects_a_repeated_descriptor() {
2753 let err = match syn::parse2::<Args>(quote!(descriptor = "a", descriptor = "b")) {
2754 Ok(_) => panic!("a repeated `descriptor` must be refused"),
2755 Err(e) => e.to_string(),
2756 };
2757 assert!(err.contains("more than once"), "{err}");
2758 }
2759
2760 #[test]
2762 fn rejects_what_the_re_render_would_drop() {
2763 let err = reject(quote! {
2764 pub trait IBad {
2765 fn go(&self) -> BinderResult<i32, String>;
2766 }
2767 });
2768 assert!(err.contains("exactly one type argument"), "{err}");
2769
2770 for decl in [
2771 quote!(
2772 #[oneway(false)]
2773 fn go(&self) -> BinderResult<()>;
2774 ),
2775 quote!(
2776 #[oneway = "no"]
2777 fn go(&self) -> BinderResult<()>;
2778 ),
2779 quote!(
2780 fn go(&self, #[inout(no)] v: &mut Vec<i32>) -> BinderResult<()>;
2781 ),
2782 ] {
2783 let err = reject(quote! {
2784 pub trait IBad {
2785 #decl
2786 }
2787 });
2788 assert!(err.contains("takes no arguments"), "{err}");
2789 }
2790 }
2791
2792 #[test]
2794 fn rejects_self_paths() {
2795 for decl in [
2797 quote!(
2798 fn go(&self, cfg: &self::Config) -> BinderResult<()>;
2799 ),
2800 quote!(
2801 fn go(&self, v: Vec<self::Cfg>) -> BinderResult<()>;
2802 ),
2803 quote!(
2804 fn go(&self) -> BinderResult<Option<Vec<self::pair::Pair<Cfg>>>>;
2805 ),
2806 ] {
2807 let err = reject(quote! {
2808 pub trait IBad {
2809 #decl
2810 }
2811 });
2812 assert!(err.contains("`self::`"), "{err}");
2813 }
2814 }
2815
2816 #[test]
2818 fn rejects_the_reserved_name_prefix() {
2819 for tokens in [
2820 quote! {
2821 pub trait __RsbPool {
2822 fn go(&self) -> BinderResult<()>;
2823 }
2824 },
2825 quote! {
2826 pub trait IBad {
2827 fn go(&self, v: &__RsbService) -> BinderResult<()>;
2828 }
2829 },
2830 quote! {
2831 pub trait IBad {
2832 fn go(&self) -> BinderResult<Vec<__RsbRuntime>>;
2833 }
2834 },
2835 quote! {
2836 pub trait IBad {
2837 fn go(&self, v: &rsbinder::Strong<dyn __RsbAsyncWrapper>) -> BinderResult<()>;
2838 }
2839 },
2840 quote! {
2841 pub trait IBad {
2842 fn go(&self, #[inout] v: &mut [__RsbPool; 3]) -> BinderResult<()>;
2843 }
2844 },
2845 ] {
2846 let err = reject(tokens);
2847 assert!(err.contains("reserved"), "{err}");
2848 }
2849 render(quote! {
2850 pub trait IOk {
2851 fn go(&self, v: &crate::m::__RsbService) -> BinderResult<()>;
2852 }
2853 });
2854 }
2855
2856 #[test]
2858 fn rejects_self_in_a_signature() {
2859 for decl in [
2860 quote!(
2861 fn go(&self, other: &Self) -> BinderResult<()>;
2862 ),
2863 quote!(
2864 fn go(&self, v: &[Self]) -> BinderResult<()>;
2865 ),
2866 quote!(
2867 fn go(&self) -> BinderResult<Option<Self>>;
2868 ),
2869 quote!(
2870 fn go(&self, v: Vec<Self>) -> BinderResult<()>;
2871 ),
2872 ] {
2873 let err = reject(quote! {
2874 pub trait IBad {
2875 #decl
2876 }
2877 });
2878 assert!(err.contains("cannot name `Self`"), "{err}");
2879 }
2880 }
2881
2882 #[test]
2884 fn qualified_vec_is_still_a_length_carrying_out_vector() {
2885 let bare = render(quote! {
2886 pub trait IOut {
2887 fn fill(&self, values: &mut Vec<i32>) -> BinderResult<()>;
2888 }
2889 });
2890 let qualified = render(quote! {
2891 pub trait IOut {
2892 fn fill(&self, values: &mut std::vec::Vec<i32>) -> BinderResult<()>;
2893 }
2894 });
2895 assert!(bare.contains("write_slice_size"), "{bare}");
2896 assert!(
2897 qualified.contains("write_slice_size"),
2898 "a qualified `Vec` must still write the length word:\n{qualified}"
2899 );
2900 assert!(qualified.contains("resize_out_vec"), "{qualified}");
2901 }
2902
2903 #[test]
2905 fn qualified_paths_keep_the_nullable_and_fd_guards() {
2906 let s = render(quote! {
2907 pub trait IOut {
2908 fn fill(&self, v: &mut core::option::Option<std::vec::Vec<i32>>)
2909 -> BinderResult<()>;
2910 }
2911 });
2912 assert!(s.contains("resize_nullable_out_vec"), "{s}");
2913
2914 let s = render(quote! {
2915 pub trait IOut {
2916 fn fds(
2917 &self,
2918 v: &mut std::vec::Vec<Option<rsbinder::ParcelFileDescriptor>>,
2919 ) -> BinderResult<()>;
2920 }
2921 });
2922 assert!(
2923 s.contains("iter().any(::core::option::Option::is_none)"),
2924 "{s}"
2925 );
2926 }
2927
2928 #[test]
2930 fn qualified_nullable_in_arguments_borrow_through() {
2931 let s = render(quote! {
2932 pub trait IIn {
2933 fn a(&self, x: std::option::Option<&str>) -> BinderResult<()>;
2934 fn b(&self, x: std::option::Option<&Cfg>) -> BinderResult<()>;
2935 fn c(&self, x: core::option::Option<&[u8]>) -> BinderResult<()>;
2936 }
2937 });
2938 for (method, call) in [
2939 ("a", "_arg_x.as_deref()"),
2940 ("b", "_arg_x.as_ref()"),
2941 ("c", "_arg_x.as_deref()"),
2942 ] {
2943 assert!(
2944 s.contains(&format!("_service.r#{method}({call})")),
2945 "{method}: {s}"
2946 );
2947 }
2948 }
2949
2950 #[test]
2952 fn by_value_argument_is_asserted_copy() {
2953 let item: ItemTrait = syn::parse2(quote! {
2954 pub trait IBad {
2955 fn go(&self, mode: super::Mode::Mode) -> BinderResult<()>;
2956 }
2957 })
2958 .unwrap();
2959 let expanded = expand(&Args { descriptor: None }, &item)
2960 .expect("expand")
2961 .to_string();
2962 assert!(
2964 expanded.contains("T : :: core :: marker :: Copy"),
2965 "{expanded}"
2966 );
2967 assert!(
2968 expanded.contains("__rsbinder_assert_copy :: < super :: Mode :: Mode > ()"),
2969 "{expanded}"
2970 );
2971 }
2972
2973 #[test]
2975 fn copy_assertion_shares_the_generated_module_scope() {
2976 let item: ItemTrait = syn::parse2(quote! {
2977 pub trait IScoped {
2978 fn go(&self, mode: super::Mode::Mode) -> BinderResult<()>;
2979 }
2980 })
2981 .unwrap();
2982 let expanded = expand(&Args { descriptor: None }, &item).expect("expand");
2983 let file: syn::File = syn::parse2(expanded).expect("expansion parses");
2984 let module = file
2985 .items
2986 .iter()
2987 .find_map(|i| match i {
2988 syn::Item::Mod(m) => Some(m),
2989 _ => None,
2990 })
2991 .expect("generated module");
2992 let body = quote!(#module).to_string();
2993 assert!(body.contains("__rsbinder_assert_copy"), "{body}");
2994 }
2995
2996 #[test]
2998 fn generated_module_follows_the_trait_visibility() {
2999 let item: ItemTrait = syn::parse2(quote! {
3000 trait IPrivate {
3001 fn go(&self) -> BinderResult<()>;
3002 }
3003 })
3004 .unwrap();
3005 let expanded = expand(&Args { descriptor: None }, &item).expect("expand");
3006 let file: syn::File = syn::parse2(expanded).expect("expansion parses");
3007 let module = file
3008 .items
3009 .iter()
3010 .find_map(|i| match i {
3011 syn::Item::Mod(m) => Some(m),
3012 _ => None,
3013 })
3014 .expect("generated module");
3015 assert!(
3016 matches!(module.vis, syn::Visibility::Inherited),
3017 "a private trait must not get a `pub` module"
3018 );
3019 }
3020
3021 #[test]
3023 fn interfaces_sharing_a_stem_declare_the_same_marker() {
3024 let marker = |tokens| {
3025 let item: ItemTrait = syn::parse2(tokens).unwrap();
3026 let expanded = expand(&Args { descriptor: None }, &item).expect("expand");
3027 let file: syn::File = syn::parse2(expanded).expect("expansion parses");
3028 file.items
3029 .iter()
3030 .find_map(|i| match i {
3031 syn::Item::Const(c) => Some(c.ident.to_string()),
3032 _ => None,
3033 })
3034 .expect("marker const")
3035 };
3036 let prefixed = marker(quote!(
3037 pub trait IFoo {
3038 fn go(&self) -> BinderResult<()>;
3039 }
3040 ));
3041 let bare = marker(quote!(
3042 pub trait Foo {
3043 fn go(&self) -> BinderResult<()>;
3044 }
3045 ));
3046 assert_eq!(prefixed, "__rsbinder_one_interface_per_stem_Foo");
3047 assert_eq!(prefixed, bare);
3048 let kept = marker(quote!(
3050 pub trait Ifoo {
3051 fn go(&self) -> BinderResult<()>;
3052 }
3053 ));
3054 assert_eq!(kept, "__rsbinder_one_interface_per_stem_Ifoo");
3055 }
3056}