Skip to main content

rsbinder_macros/
lib.rs

1// Copyright 2026 Jeff Kim <hiking90@gmail.com>
2// SPDX-License-Identifier: Apache-2.0
3
4//! Define a Binder interface as a Rust trait — no `.aidl`, no `build.rs`.
5//!
6//! ```ignore
7//! use rsbinder::{interface, BinderResult, Strong, Uri};
8//!
9//! #[interface(descriptor = "com.example.IHello")]
10//! pub trait IHello {
11//!     fn echo(&self, msg: &str) -> BinderResult<String>;
12//!     fn fill(&self, out: &mut Vec<i32>) -> BinderResult<()>;   // `&mut` = out
13//!     #[oneway] fn ping(&self) -> BinderResult<()>;
14//! }
15//!
16//! rsbinder::serve(Uri::kernel())?.add("hello", BnHello::new_binder(Impl))?.run()?;
17//! let h: Strong<dyn IHello> = rsbinder::connect(Uri::kernel().with_service("hello"))?;
18//! ```
19//!
20//! # Relationship to `.aidl`
21//!
22//! `.aidl` stays the canonical path (plan 2-17 D3): it is what Android tooling
23//! reads, what other languages generate from, and the only path that carries
24//! AIDL-only semantics. This macro is for **rsbinder ↔ rsbinder** interfaces,
25//! where the wire is private to both ends.
26//!
27//! It is not a re-implementation. The macro fills the very same
28//! [`rsbinder_aidl::render`] structs the AIDL front-end fills and runs the same
29//! templates, so a trait here and the equivalent `.aidl` produce **identical**
30//! generated code — moving from one to the other never touches a call site.
31//! What it cannot express (unions, constants, `@VintfStability`,
32//! `@EnforcePermission`, `ParcelableHolder`, generics, nested types) is what
33//! needs AIDL semantics; write those in `.aidl`.
34//!
35//! # What the signature means
36//!
37//! | Signature | Meaning |
38//! |---|---|
39//! | `x: i32` — an AIDL scalar, or a bare name that may be an enum (asserted `Copy`) | in argument, by value |
40//! | `x: &Cfg`, `x: &str`, `x: &[T]` — every other `in` type borrows | in argument |
41//! | `x: &mut T` | **out** argument |
42//! | `#[inout] x: &mut T` | written **and** read back |
43//! | `#[deprecated]` / `#[deprecated = "…"]` | AIDL's `@deprecated`, on a trait or a method |
44//! | `Option<T>` | nullable |
45//! | `#[oneway]` on a method | no reply; must return `BinderResult<()>` |
46//!
47//! `Option<T>` is AIDL's `@nullable`, which AIDL allows only on the types that
48//! have a null representation on the wire. A `#[derive(BinderEnum)]` enum is
49//! carried as its `repr` scalar and is not one of them: `Option<Mode>` fails to
50//! compile inside the generated code, because the enum has no `SerializeOption`
51//! — the same shape `.aidl` rejects at the AIDL level. A primitive has no null
52//! either, so `Option<i32>` is refused outright, wherever it sits.
53//!
54//! **AIDL's scalars are a fixed set.** `bool`, `i8` (`byte`), `i32` (`int`),
55//! `i64` (`long`), `f32` (`float`), `f64` (`double`) and `u16` (`char`) are the
56//! only scalar spellings `.aidl` renders, joined by `u8` as the element of a
57//! `byte[]` (`&[u8]`). `byte` is the one type whose spelling moves with the
58//! place — `i8` as a scalar, `u8` as an array element — and neither is
59//! accepted in the other's place. Any other Rust scalar — `u32`, `u64`, `i16`,
60//! `usize`, `u128`, Rust's own `char`, or a `u8` outside an array — is refused
61//! wherever it sits, argument, return or `#[derive(Parcelable)]` field: the
62//! nearest `.aidl` renders a *different* Rust type, so moving the interface to
63//! `.aidl` would break every call site, which is the one thing this macro
64//! promises it will not do. `u128` is worse than a mismatch — it writes
65//! sixteen bytes no conforming AIDL peer can decode.
66//!
67//! **Not everything can be an out parameter.** AIDL passes a primitive, a
68//! `String`, an enum, an `IBinder` and an interface `in` only, and a
69//! `ParcelFileDescriptor` `in` or `inout` (AOSP `GetArgumentAspect`). Each
70//! `&mut` in a direction AIDL does not permit is refused — `&mut String` even
71//! as `&mut Option<String>`, because `@nullable` does not widen the direction,
72//! and a `&mut ParcelFileDescriptor` unless it is `#[inout]` — except for the
73//! enum: the macro sees only its name and cannot tell
74//! `&mut Mode` from a parcelable's `&mut Config`, which is a legal out
75//! parameter. Never take an enum by `&mut`; no `.aidl` can express it. An
76//! array of any of these takes every direction.
77//!
78//! A binder object (`Strong<dyn IFoo>`, `SIBinder`) and a
79//! `ParcelFileDescriptor` have no `Default` for the callee to start from, so
80//! `.aidl` wraps them in `Option<_>` wherever there is nothing to start from —
81//! a `#[derive(Parcelable)]` field and the slots of an array the callee must
82//! default. The table below has the exact spelling for every one of those;
83//! what matters here is that the `Option` is `.aidl`'s own and not a
84//! `@nullable` you wrote, and that an `out` fd array left `None` fails the
85//! call with `UNEXPECTED_NULL`, a null having no valid fd encoding. A
86//! `ParcelableHolder` is a parcelable field type only and cannot appear in a
87//! signature at all.
88//!
89//! **An out vector is an array, never a `List`.** An out `&mut Vec<T>` is
90//! `.aidl`'s `out T[]`, and `&mut Option<Vec<T>>` its `out @nullable T[]`: the
91//! proxy sends the caller's current length (null for `None`) and the service
92//! starts from a vector pre-sized to it. `.aidl`'s `out List<T>` renders the
93//! very same signature but sends no length and starts the service from an
94//! empty vector (`None` when `@nullable`), so the two are not wire-compatible.
95//! `out List<T>` has no spelling here; it needs `.aidl`.
96//!
97//! **A `@nullable` array wraps its elements** unless the element is a
98//! primitive or an enum, in every direction and at every arity. A
99//! non-`@nullable` array wraps them only where the slot starts empty: an `out`
100//! array, or a fixed-size parcelable field, of a binder, an interface or a fd.
101//! Every cell of the rule is in the table below, which is generated from the
102//! rules and checked against them.
103//!
104//! One cell no table can decide for you. A `#[derive(BinderEnum)]` element
105//! counts as a primitive and stays bare; a parcelable element takes an
106//! `Option<_>`. The macro sees a name and not which of the two it is, so it
107//! accepts either and cannot tell you which is right — spell an enum element
108//! bare and a parcelable element `Option<_>`, as `.aidl` renders them.
109//!
110//! **A `Box` appears in one place only.** `.aidl` boxes a parcelable field
111//! that is `@nullable(heap=true)`, or a bare `@nullable` one that closes a
112//! reference cycle — `Option<Box<T>>` — and nothing else, so a `Box` anywhere
113//! else is refused, including every argument and return: a cycle is judged
114//! against the declaration being generated, and an interface is never part of
115//! one. Which `.aidl` field that spelling stands for depends on annotations and
116//! declarations the macro cannot see, so it accepts it on any parcelable field.
117//!
118//! **Paths resolve inside the generated module.** The body lands in a
119//! `{Trait}_binder` module one level below where the macro was written, and it
120//! reaches the surrounding scope through a `use super::*;`. A bare name is
121//! therefore the user's own — but `super::X` names the module the macro was
122//! written in, not its parent, so a signature that means the parent has to say
123//! `crate::X`. `self::` is rejected outright for the same reason; `super::` is
124//! left legal because it is the only way to name a type the generated module
125//! shadows (`BnFoo`, `BpFoo`, `transactions`, the trait's own name). Use the
126//! macro at module scope: inside a function body `super::*` skips the block,
127//! so items declared in that block are not visible to the signature.
128//!
129//! The generated body names prelude items by absolute path, so a parent item
130//! called `Ok`, `Option` or `Default` reaches only the signatures that name
131//! it. Two names still resolve in the module: `Box`, which `async-trait`'s
132//! expansion uses bare under the `async` feature, and the owned `String` /
133//! `Vec<T>` the stub declares for a `&str` / `&[T]` argument. The `async` half
134//! declares its own type parameters and helper struct next to the signatures,
135//! all named with the `__Rsb` prefix, so the macro refuses a trait name that
136//! starts with it and a signature path whose first segment does (`crate::…`
137//! still reaches such an item). A parent item named `rsbinder` takes over the
138//! generated code's `rsbinder::` paths; the last paragraph of
139//! [What it emits](#what-it-emits) says why.
140//!
141//! **Spell types directly.** A proc macro cannot see through a type alias or
142//! a `use … as` rename, and every check above reads the spelling. `&mut Ids`
143//! for `type Ids = Vec<i32>` reads as an opaque named type: it loses the
144//! length word `.aidl` writes for an out array (`T[]`), and an aliased
145//! `ParcelFileDescriptor` element loses the null guard that keeps a null fd
146//! off the wire. Likewise a renamed `ParcelableHolder` or an alias of
147//! `Option<i32>` slips past the check that refuses it and generates the code
148//! that check exists to prevent, and a renamed `Strong` or
149//! `ParcelFileDescriptor` escapes the out-parameter rule above. Path
150//! qualification is fine — `std::vec::Vec<i32>` is matched structurally — but
151//! an alias or a rename is not.
152//!
153//! A doc comment on the trait or a method is accepted but not carried: the
154//! render layer has no doc field, so the emitted `IFoo`, its methods and
155//! `BnFoo`/`BpFoo` carry no rustdoc from here.
156//!
157//! # What it emits
158//!
159//! The generated items — the trait, `BnFoo`, `BpFoo`, `IFooDefault`, and with
160//! the `async` feature the `IFooAsync` halves — land in the module where the
161//! macro was written, so they are usable without a path. They physically live
162//! in a `#[doc(hidden)] mod {Trait}_binder` that is glob re-exported: the
163//! generated body reuses `.aidl`'s fixed internal names (`transactions`,
164//! `on_transact`, `DEFAULT_IMPL`), which would collide if two interfaces were
165//! emitted side by side. That module is an implementation detail — never name
166//! it. `IFoo` and `Foo` both name their halves `BnFoo`/`BpFoo` (AOSP
167//! `ClassName`), so one module holds at most one of them: a second fails with
168//! E0428 on `__rsbinder_one_interface_per_stem_Foo`. Put them in separate
169//! modules.
170//!
171//! Transaction codes are assigned by declaration order
172//! (`FIRST_CALL_TRANSACTION + i`), exactly as `.aidl` does without explicit
173//! codes — so **reordering methods is a wire break**. The descriptor defaults
174//! to the bare trait name; pass `descriptor = "…"` for anything shared across
175//! crates.
176//!
177//! The generated code names `rsbinder::` directly, so the dependency has to
178//! keep that name — a `package = "rsbinder"` rename will not resolve. The
179//! path is relative, not `::rsbinder::`: the generated module glob-imports the
180//! module the macro was written in, so a `mod rsbinder` or `use … as rsbinder`
181//! there is what every generated `rsbinder::` path resolves to.
182
183// Generated; `type_matrix::the_reference_table_matches_the_rules` fails when it falls behind.
184#![doc = include_str!("../TYPES.md")]
185// Library code returns errors; tests may unwrap (plan 13).
186#![cfg_attr(not(test), deny(clippy::unwrap_used))]
187// Every allow outside tests says why (plan 13-1).
188#![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/// Argument direction, read off the Rust signature.
208#[derive(Clone, Copy, PartialEq, Eq)]
209enum Dir {
210    In,
211    Out,
212    Inout,
213}
214
215/// `#[interface(descriptor = "…")]`.
216struct 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                // Spliced unescaped into a generated literal; `.aidl`'s grammar never admits these.
246                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                // Shared by `#[interface]` and `#[parcelable]`: the name a peer compares.
257                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
276/// `#[<name>(descriptor = "…")]` on a derive input.
277fn 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/// Parcel codec for a plain Rust struct — the `.aidl`-free `parcelable`.
298///
299/// Fields are the wire, in declaration order, so **reordering or inserting a
300/// field is a wire break**. The emitted `write_to_parcel` / `read_from_parcel`
301/// are the same ones `rsbinder-aidl` emits for the equivalent `parcelable`,
302/// including the size-prefixed header and the truncated-read handling that
303/// lets an older reader accept a newer writer's extra fields.
304///
305/// Only the codec is generated, so `Debug`, `Clone` and friends stay yours to
306/// derive — but **`Default` is required**: reading a parcelable that arrives
307/// as `null` builds one from it. `#[derive(Parcelable, Default, Debug)]` is
308/// the normal shape. The descriptor defaults to the type name and is
309/// overridden with `#[parcelable(descriptor = "…")]`. `#[deprecated]` and
310/// `#[deprecated = "…"]` carry through as AIDL's `@deprecated`, on the struct
311/// and on a field alike; the richer Rust forms carry fields `.aidl` has
312/// nowhere to put and are refused.
313///
314/// Fields must be named and owned — owned all the way down, so `Option<&str>`
315/// and `Vec<&str>` are out too. A field passes the very same type gates a
316/// signature does, reached through one entry point rather than a second copy,
317/// so the crate-level docs' scalar set (`u128` and friends refused, `byte`
318/// spelled `i8` as a scalar and `u8` as an array element), the array-element
319/// rules and the refusal of `()` all apply here — answered for a field, which
320/// has no direction of its own. A nullable primitive (`Option<i32>`) is
321/// refused for the same reason the interface path refuses it: `.aidl` has no
322/// `@nullable int`. A `ParcelableHolder` field is an `.aidl`-only shape. A
323/// binder or fd field (`Strong<dyn IFoo>`, `SIBinder`,
324/// `ParcelFileDescriptor`) has to be spelled `Option<_>`: the field has no
325/// value to start from, so `.aidl` renders it that way whether or not it is
326/// `@nullable`, and the bare spelling is refused. Here that `Option<_>` is
327/// AIDL's `@nullable`, even though `.aidl` spells the non-nullable field the
328/// same way.
329///
330/// These checks read the field's spelling — a proc macro cannot see through a
331/// type alias or a `use … as` rename. A `ParcelableHolder` imported under
332/// another name, or a `type MaybeInt = Option<i32>`, is not caught and
333/// compiles into the very codec the check exists to refuse; spell them
334/// directly.
335#[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/// Parcel codec for a plain Rust enum, carried as its `#[repr(..)]` scalar.
345///
346/// `#[repr(i8)]`, `#[repr(i32)]` or `#[repr(i64)]` is required — it is the
347/// wire format (AIDL `byte`, `int`, `long`) — and every variant needs an
348/// explicit value, so what goes on the wire is visible at the declaration
349/// instead of implied by declaration order.
350///
351/// ```ignore
352/// #[derive(BinderEnum, Clone, Copy, PartialEq, Eq, Debug)]
353/// #[repr(i32)]
354/// pub enum Mode { Fast = 0, Safe = 1 }
355/// ```
356///
357/// Nothing beyond the `repr` is required of the type — the codec matches on
358/// the variant rather than casting through a shared reference.
359///
360/// The enum is not nullable: `Option<Mode>` has no wire form here, matching
361/// `.aidl`, where `@nullable` on an enum is rejected outright.
362///
363/// **This enum is closed.** A value no variant declares deserializes to
364/// `rsbinder::StatusCode::BadValue`. An `.aidl` enum is open: its generated
365/// newtype keeps whatever a newer peer sent, so a reader can pass an unknown
366/// value along untouched. Use `.aidl` — or `rsbinder::declare_binder_enum!`,
367/// which emits exactly that newtype — when the two ends may be different
368/// versions. Within one build of both ends, a real Rust enum is the better
369/// type: it matches exhaustively and cannot hold a value you never defined.
370#[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/// Binder service-specific error codes for a plain Rust enum.
380///
381/// Implements `rsbinder::ServiceSpecificError`, so the type can be passed to
382/// `Status::service_specific` and read back with
383/// `Status::service_error::<T>()` instead of a bare `i32`.
384///
385/// ```ignore
386/// #[derive(ServiceSpecificError, Clone, Copy, PartialEq, Eq, Debug)]
387/// #[repr(i32)]
388/// pub enum LookupError { NotFound = 1, Busy = 2 }
389///
390/// Err(Status::service_specific(LookupError::Busy, Some("try again")))
391/// ```
392///
393/// `#[repr(i8)]`, `#[repr(i16)]` or `#[repr(i32)]` is required, and every
394/// variant needs an explicit value — it is the number a peer matches on, so
395/// it belongs at the declaration rather than in declaration order. A wider
396/// repr is refused here rather than truncated on the wire, where a binder
397/// status carries the code as an `i32`.
398///
399/// This derive does not touch the parcel: an error code travels in the
400/// status header, not as a value in the payload. Combine it with
401/// `#[derive(BinderEnum)]` when the same enum is also a method argument or
402/// return value.
403///
404/// For an `.aidl` enum, use `rsbinder::impl_service_specific_error!` instead
405/// — the generated type is an open newtype, not a Rust enum, so a derive
406/// cannot be attached to it. Never both on one type: two impls of one trait
407/// do not compile.
408#[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/// A binder interface from a plain Rust trait — the `.aidl`-free `interface`.
418///
419/// Emits what `rsbinder-aidl` emits for the equivalent `.aidl` interface: the
420/// trait, `BnFoo`, `BpFoo` and `IFooDefault` (and the async halves with the
421/// `async` feature). Transaction codes follow declaration order, so
422/// **reordering methods is a wire break**. The descriptor defaults to the
423/// trait name; pass `#[interface(descriptor = "…")]` for anything shared
424/// across crates.
425///
426/// ```ignore
427/// #[rsbinder::interface(descriptor = "my.pkg.IHello")]
428/// pub trait IHello {
429///     fn echo(&self, msg: &str) -> rsbinder::BinderResult<String>;
430/// }
431/// ```
432///
433/// Each signature must be the spelling `.aidl` renders for its type and
434/// direction, and anything else is refused with that spelling named. The
435/// full contract — directions, `#[oneway]`, `#[inout]`, the
436/// type table and what the macro cannot express — is the
437/// [crate documentation](https://docs.rs/rsbinder-macros).
438#[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    // Renamed: a module named like the trait would shadow the trait's glob re-export.
466    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    // A `pub` module would make a private trait nameable from outside.
473    module.vis = item.vis.clone();
474
475    // The proxy passes a by-value argument twice, so assert `Copy` where the body resolves it.
476    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    // `IFoo` and `Foo` both glob-export `BnFoo`; this turns the later E0659 into E0428 here.
495    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
514/// `Ident`'s `Display` keeps the `r#`, and the templates add their own.
515pub(crate) fn strip_raw(ident: &str) -> &str {
516    ident.strip_prefix("r#").unwrap_or(ident)
517}
518
519/// The generated module source, split out for the golden test (plan 2-19 D1).
520fn 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    // A trait-level `#[cfg]` or `#[oneway]` would vanish in the re-render.
526    check_attrs(&item.attrs, &["deprecated"])?;
527    // The render layer carries no trait modifier, so an accepted one would vanish.
528    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    // `#[non_exhaustive]`: a modifier a later `syn` adds is refused instead of dropped.
542    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    // `r#type` and `type` are one name.
570    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        // The `.aidl` path refuses the same names (`rsbinder-aidl` `Generator::decl_interface`).
586        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        // AOSP `AidlInterface::CheckValid` `reserved_methods`, matched by signature.
597        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    // `#[non_exhaustive]`, like the trait's: the re-render carries none of them.
637    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    // The re-render drops these, so the user's `impl` would miss its own signature.
657    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    // `Safety::Safe` parses only inside an `extern` block.
665    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    // `syn` parses `...` even outside an `extern` block; the render layer would drop it.
684    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    // `&'_ self` is `&self`; a named one would be dropped by the re-render.
705    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        // `rsbinder-aidl`'s `_arg_` prefix; trait parameter names do not bind the `impl`.
735        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        // AOSP `AidlMethod::CheckValid`; else E0415 lands on generated tokens.
739        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        // Structural, not string: `std::vec::Vec<T>` carries the same length word as `Vec<T>`.
771        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            // Only the length is sent; the server sizes from it (AOSP `resizeOutVector`).
784            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            // A `None` fd element would go out as a null fd; `.aidl` raises `UNEXPECTED_NULL`.
810            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
850/// Bridge `owned` to `as_written` off the signature alone, so no type list is needed.
851fn 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    // Structural, so `std::option::Option<&str>` borrows through like `Option<&str>`.
865    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
928/// Argument types the signature takes by value.
929fn 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
947/// `#[deprecated]` / `#[deprecated = "…"]` as `.aidl` renders `@deprecated`.
948pub(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    // The input item is not re-emitted, so rustc's own E0550 never sees the second one.
954    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(&note)))
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
988/// The re-render drops an unrecognised attribute, e.g. a mistyped `#[oneway]` or a `#[cfg]`.
989fn check_attrs(attrs: &[syn::Attribute], allowed: &[&str]) -> syn::Result<()> {
990    for attr in attrs {
991        // Only `#[doc = "…"]`: a `#[doc(hidden)]` has no field to survive the re-render in.
992        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            // `#[deprecated]` carries its note; `deprecated_of` checks its shape.
997            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    //! A trait here and the equivalent `.aidl` must render the same module source (plan 2-19 D1).
1035
1036    use super::*;
1037    use std::path::{Path, PathBuf};
1038    use std::sync::atomic::{AtomicU32, Ordering};
1039
1040    /// A temp directory that removes itself on drop.
1041    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    /// The interface module `rsbinder-aidl` generates from `aidl`, without the file header.
1068    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    /// `pub mod {module} { … }`, dedented: a packaged `.aidl` nests it, the macro does not.
1087    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    /// Both `enabled_async` settings: a `cfg!` would leave one half unchecked.
1128    #[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    /// [`assert_same`] for a multi-file fixture carrying an explicit descriptor.
1167    #[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    /// [`from_aidl`] with sibling files, so a fixture can reference another interface.
1194    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    /// `out List<T>` renders the same signature without the length word, so pin the array side.
1282    #[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        // Cross-file, to pin the `super::Mod::Type` path a packaged import produces.
1329        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    /// A fixed-size out fd array keeps the guard, one `.flatten()` per extra dimension.
1360    #[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    /// Every out/inout shape `.aidl` renders, in one interface.
1391    #[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    /// `@deprecated` on the trait and the method renders what `.aidl` renders.
1523    #[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    /// The richer Rust forms carry fields `.aidl` has nowhere to put.
1551    #[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    /// Only the first would render; the rest would vanish without a word.
1563    #[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    /// `#[doc(hidden)]` has nowhere to go in the render, so it would be dropped.
1576    #[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        // A doc comment is `#[doc = "…"]`, and stays legal.
1588        render(quote! {
1589            /// Documented.
1590            pub trait IOk {
1591                /// Documented too.
1592                fn go(&self) -> BinderResult<()>;
1593            }
1594        });
1595    }
1596
1597    /// Advice for a nullable bare path must keep the `Option`: `@nullable Mode` has no form.
1598    #[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    /// Every `in` array shape `.aidl` renders; the element `Option` rule differs from `out`.
1610    #[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    /// An element `Option` `.aidl` spells bare lets a null go out that a peer cannot decode.
1689    #[test]
1690    fn rejects_an_array_of_options_aidl_would_spell_bare() {
1691        for decl in [
1692            // `in` never gives an element its own `Option`.
1693            quote!(
1694                fn go(&self, v: &[Option<rsbinder::ParcelFileDescriptor>]) -> BinderResult<()>;
1695            ),
1696            // `out` gives one only to a binder or a fd.
1697            quote!(
1698                fn go(&self, v: &mut Vec<Option<String>>) -> BinderResult<()>;
1699            ),
1700            // A variable `inout` array is read in fully populated.
1701            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    /// Only `out` starts a slot empty, fixed `#[inout]` included; `@nullable` wraps everywhere.
1718    #[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            // A fixed-size `in` array wraps them like a variable one.
1767            (
1768                quote!(
1769                    fn go(
1770                        &self,
1771                        v: Option<&[rsbinder::ParcelFileDescriptor; 3]>,
1772                    ) -> BinderResult<()>;
1773                ),
1774                "a nullable `in` array",
1775            ),
1776            // A return renders down the same arm as `in`, both ways round.
1777            (
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        // The spellings `.aidl` renders for those same shapes.
1810        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    /// `.aidl` has no variable-length dimension beside another, nor an `Option` around one.
1830    #[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    /// The spellings `.aidl` never renders for an `in` argument.
1883    #[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        // `&mut Vec<T>` is the out spelling, and stays legal.
1903        render(quote! {
1904            pub trait IOk {
1905                fn go(&self, v: &mut Vec<i32>) -> BinderResult<()>;
1906            }
1907        });
1908    }
1909
1910    /// The return type takes the argument guards too, minus the `()` it may be.
1911    #[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    /// A scalar `.aidl` cannot render makes the `.aidl` port a different type.
1927    #[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            // `byte` is `u8` in an array element, so `i8` is wrong exactly there.
1961            (
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            // Below a generic other than `Vec`/`Option` the argument is still a scalar.
1980            (
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        // Every scalar `.aidl` does render, plus `u8` as a `byte[]` element.
2001        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    /// `.aidl` boxes only the cycle-closing `@nullable` parcelable field, at its outer `Option`.
2013    #[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    /// `.aidl` refuses an array, `List` or `@nullable` as a user generic's type argument.
2069    #[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    /// An empty descriptor would let two interfaces cast to each other's proxy.
2111    #[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    /// The one shape where the two paths could disagree on how an interface names itself.
2121    #[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    /// Compared before the derive drops the struct and `Default`, so the whole module is pinned.
2144    #[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    /// An enum is a scalar in `.aidl`, so it is passed by value.
2188    #[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    /// The `_arg_` prefix and the templates' `r#` must not stack into `_arg_r#` / `r#r#`.
2296    #[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    /// A raw trait name has to survive into `Bn`/`Bp` and the module name.
2311    #[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    /// The re-render would drop these: a `#[cfg]` ignored, a `#[oneway]` turned twoway.
2327    #[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    /// The re-render drops modifiers, so the user's `impl` would miss its own signature.
2364    #[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    /// `syn` keeps `&mut self`'s `mut` inside the receiver kind, so the check names the kind.
2382    #[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        // Already a shared borrow, so the reason has to be the lifetime.
2399        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        // `&'_ self` is the same type as `&self`, so nothing is lost.
2406        render(quote! {
2407            pub trait IOk {
2408                fn go(&'_ self) -> BinderResult<()>;
2409            }
2410        });
2411    }
2412
2413    /// A receiver's attribute is dropped by the re-render like any other.
2414    #[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    /// As a return type, `Option<&str>` would silently become `Option<String>`.
2436    #[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    /// `@nullable` does not widen a `String`'s direction.
2447    #[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    /// AOSP `GetArgumentAspect`: `IBinder` and an interface `in` only, a fd `in` or `inout`.
2467    #[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        // The attribute that marked a non-`@nullable` `out` binder or fd has nothing left to mark.
2506        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        // A fd `inout`, and an array of either in any direction, are `.aidl` forms.
2513        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    /// Each element of an out array needs a `Default` to be sized or started from.
2525    #[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        // `.aidl`'s non-nullable `inout` array is the bare form.
2542        render(quote! {
2543            pub trait IOk {
2544                fn a(&self, #[inout] v: &mut Vec<rsbinder::ParcelFileDescriptor>)
2545                    -> BinderResult<()>;
2546            }
2547        });
2548    }
2549
2550    /// `macro_rules!` hands a `$t:ty` over wrapped in an invisible group.
2551    #[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    /// A whole interpolated parameter or return type keeps its direction and shape.
2571    #[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    /// No wire form; rustc would otherwise blame generated tokens.
2594    #[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    /// `&[Option<i32>]` is the `Vec<Option<i32>>` shape, and as inexpressible.
2616    #[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            // By value: `&Vec<T>` is refused ahead of this for its own reason.
2622            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    /// `rsbinder-aidl` refuses a holder in a signature, however wrapped.
2634    #[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    /// A duplicate would collide in generated tokens, away from the user's span.
2657    #[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        // The `r#` is not part of the name: the templates add their own.
2667        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    /// A generated trait item's name is taken for every signature, as on the `.aidl` path.
2684    #[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        // Raw spelling is the same name.
2707        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    /// AOSP reserves these by signature, so a `.aidl` port would have to rename them.
2716    #[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        // Another signature is another method to AOSP.
2728        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    /// The receiver allows nothing; an empty allow-list must not render `understands only`.
2737    #[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    /// The descriptor is the wire name, so a second one is refused, not picked.
2751    #[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    /// The re-render reads no attribute argument and no second type argument.
2761    #[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    /// `self::` would resolve inside the generated module, not the user's.
2793    #[test]
2794    fn rejects_self_paths() {
2795        // The non-canonical ones must not first be told a `self::` spelling to use.
2796        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    /// `__Rsb*` names are the template's own; `crate::`-anchored paths stay legal.
2817    #[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    /// A `Self` outside the receiver makes the generated trait unusable as `dyn IFoo`.
2857    #[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    /// Matched structurally, so a qualified `Vec` carries the same length word.
2883    #[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    /// Same for `Option<Vec<T>>`, and for the guard that keeps a null fd off the wire.
2904    #[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    /// A qualified nullable `in` borrows through as the bare `Option<&T>` does.
2929    #[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    /// The `Copy` assertion refuses a by-value parcelable; pinned here as rustc's wording moves.
2951    #[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        // Both the `Copy` bound and the asserted type, so neither can be weakened unnoticed.
2963        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    /// The assertion names signature types, so it must resolve them where the body does.
2974    #[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    /// `#[doc(hidden)]` hides the module from docs, not from paths.
2997    #[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    /// `IFoo` and `Foo` share `BnFoo`; both declare the same marker, so the clash is E0428.
3022    #[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        // `Ifoo` keeps its `I` (AOSP `ClassName`), so it does not clash with `foo`.
3049        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}