Skip to main content

miniextendr_macros/
externalptr_args.rs

1//! Borrowed external arguments (#2106): `&T` / `&mut T` for a `TypedExternal`
2//! type, the views `ExternalPtrRef<'_, T>` / `ExternalPtrMut<'_, T>`, and an
3//! `Option` of either.
4//!
5//! The wrapper converts the argument to its `ExternalPtr<T>` handle with the
6//! other arguments (`from_r::__mx_external_arg`, which unwraps and roots a
7//! class handle), then borrows the value's `RefCell` the way a method receiver
8//! is borrowed: shared for `&T` and `ExternalPtrRef`, exclusive for `&mut T`
9//! and `ExternalPtrMut`. The borrow is taken after every conversion and held
10//! through return conversion, and the body gets the plain reference (or the
11//! view). One object passed twice with a `&mut T`, or a callback into R that
12//! reaches the object while the function runs, then raises the
13//! `miniextendr_borrow_conflict` error instead of making a second reference.
14//!
15//! An `ExternalPtr<T>` parameter is a handle the body keeps or consumes: it
16//! takes no borrow, and reads through it are `borrow()` / `borrow_mut()`.
17//!
18//! A borrowed argument keeps the call on R's main thread
19//! ([`crate::type_inspect::is_main_thread_bound_input`]), like a `&self`
20//! receiver.
21
22use proc_macro2::TokenStream;
23use quote::{format_ident, quote, quote_spanned};
24use syn::spanned::Spanned;
25
26/// How a borrowed external argument holds its value.
27#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for Mode { }
#[automatically_derived]
impl ::core::clone::Clone for Mode {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Mode { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for Mode {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                Mode::Shared => "Shared",
                Mode::Exclusive => "Exclusive",
                Mode::SharedView => "SharedView",
                Mode::ExclusiveView => "ExclusiveView",
            })
    }
}Debug, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for Mode { }
#[automatically_derived]
impl ::core::cmp::PartialEq for Mode {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
            ::core::intrinsics::discriminant_value(other)
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for Mode { }Eq)]
28pub(crate) enum Mode {
29    /// `&T`.
30    Shared,
31    /// `&mut T`.
32    Exclusive,
33    /// `ExternalPtrRef<'_, T>`.
34    SharedView,
35    /// `ExternalPtrMut<'_, T>`.
36    ExclusiveView,
37}
38
39impl Mode {
40    fn is_exclusive(self) -> bool {
41        #[allow(non_exhaustive_omitted_patterns)] match self {
    Mode::Exclusive | Mode::ExclusiveView => true,
    _ => false,
}matches!(self, Mode::Exclusive | Mode::ExclusiveView)
42    }
43}
44
45/// A borrowed external argument's type, taken apart.
46#[derive(#[automatically_derived]
impl ::core::fmt::Debug for ExternalArg {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f, "ExternalArg",
            "value", &self.value, "optional", &self.optional, "mode",
            &&self.mode)
    }
}Debug)]
47pub(crate) struct ExternalArg {
48    /// `T`, as written (`Self` included).
49    pub value: syn::Type,
50    /// Under `Option`: `NULL` is `None`.
51    pub optional: bool,
52    pub mode: Mode,
53}
54
55/// Names that are never an external value behind `&` / `&mut`: R's native
56/// borrows, `&str` / `&Path`, the dots, the RNG guard an `rng` wrapper passes
57/// (#2002), and containers with conversions of their own.
58const NOT_EXTERNAL: &[&str] = &[
59    "str",
60    "Path",
61    "Dots",
62    "LazyDots",
63    "RngGuard",
64    "SEXP",
65    "i32",
66    "f64",
67    "u8",
68    "RLogical",
69    "Rcomplex",
70    "Option",
71    "Vec",
72    "Box",
73    "Rc",
74    "Arc",
75    "Cow",
76    "Result",
77    "ExternalPtr",
78    "ExternalPtrRef",
79    "ExternalPtrMut",
80    "Missing",
81];
82
83/// The borrowed external argument `ty` describes, if any: `&T` / `&mut T`
84/// where `T` names a type with no R-side conversion of its own (no type
85/// check, not in [`NOT_EXTERNAL`]), or an `ExternalPtrRef<'_, T>` /
86/// `ExternalPtrMut<'_, T>` view, either one optionally under `Option`.
87/// Matched on the written type: a `&T` whose `T` isn't `TypedExternal` is a
88/// compile error at the type (`ExternalPtr<T>` requires it).
89pub(crate) fn external_arg(ty: &syn::Type) -> Option<ExternalArg> {
90    let (inner, optional) = match crate::type_inspect::option_inner_type(ty) {
91        Some(inner) => (inner, true),
92        None => (ty, false),
93    };
94    let (value, mode) = match inner {
95        syn::Type::Reference(reference) if is_external_value(&reference.elem) => {
96            let mode = if reference.mutability.is_some() {
97                Mode::Exclusive
98            } else {
99                Mode::Shared
100            };
101            (reference.elem.as_ref().clone(), mode)
102        }
103        syn::Type::Path(path) if path.qself.is_none() => {
104            let seg = path.path.segments.last()?;
105            let mode = if seg.ident == "ExternalPtrRef" {
106                Mode::SharedView
107            } else if seg.ident == "ExternalPtrMut" {
108                Mode::ExclusiveView
109            } else {
110                return None;
111            };
112            (crate::type_inspect::first_type_argument(seg)?.clone(), mode)
113        }
114        _ => return None,
115    };
116    Some(ExternalArg {
117        value,
118        optional,
119        mode,
120    })
121}
122
123fn is_external_value(ty: &syn::Type) -> bool {
124    let syn::Type::Path(path) = ty else {
125        return false;
126    };
127    path.qself.is_none()
128        && path.path.segments.last().is_some_and(|seg| {
129            !NOT_EXTERNAL.contains(&seg.ident.to_string().as_str())
130                && !crate::r_preconditions::has_type_check(ty)
131        })
132}
133
134/// The binding of the handle a borrowed argument `param` converts to.
135/// Mixed-site: user parameters may use any spelling.
136pub(crate) fn handle_ident(param: &syn::Ident) -> syn::Ident {
137    match ::quote::__private::IdentFragmentAdapter(&crate::naming::unraw(param)) {
    arg =>
        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                        ::alloc::fmt::format(format_args!("__mx_ptr_{0}", arg))
                    }),
            ::quote::__private::Option::Some::<::quote::__private::Span>(proc_macro2::Span::mixed_site())),
}format_ident!(
138        "__mx_ptr_{}",
139        crate::naming::unraw(param),
140        span = proc_macro2::Span::mixed_site()
141    )
142}
143
144/// `ty` with `Self` replaced by `self_ty` (an impl method's wrapper is a free
145/// function, where `Self` names nothing).
146pub(crate) fn replace_self(ty: &syn::Type, self_ty: Option<&syn::Type>) -> syn::Type {
147    match (ty, self_ty) {
148        (syn::Type::Path(path), Some(self_ty)) if path.path.is_ident("Self") => self_ty.clone(),
149        _ => ty.clone(),
150    }
151}
152
153/// The statement converting the borrowed argument `ident` (of type `arg`) to
154/// its handle, given its R-facing failure arm (`err_arm`, an `Err(e) =>`
155/// match arm). `self_ty` replaces `Self`.
156pub(crate) fn handle_conversion(
157    ident: &syn::Ident,
158    sexp_ident: &syn::Ident,
159    ty: &syn::Type,
160    arg: &ExternalArg,
161    self_ty: Option<&syn::Type>,
162    err_arm: TokenStream,
163) -> TokenStream {
164    let handle = handle_ident(ident);
165    if let Some(lifetime) = crate::type_inspect::static_lifetime(ty) {
166        return {
    let mut _s = ::quote::__private::TokenStream::new();
    let _span: ::quote::__private::Span =
        ::quote::__private::get_span(lifetime.span()).__into_span();
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "core");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "compile_error");
    ::quote::__private::push_bang_spanned(&mut _s, _span);
    ::quote::__private::push_group_spanned(&mut _s, _span,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            let _span: ::quote::__private::Span =
                ::quote::__private::get_span(_span).__into_span();
            ::quote::__private::parse_spanned(&mut _s, _span,
                "\"this parameter borrows its object for the call and cannot be `\'static`; \\\n                 elide the lifetime, or take `ExternalPtr<T>` to keep a handle\"");
            _s
        });
    ::quote::__private::push_semi_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "let");
    ::quote::ToTokens::to_tokens(&ident, &mut _s);
    ::quote::__private::push_colon_spanned(&mut _s, _span);
    ::quote::ToTokens::to_tokens(&ty, &mut _s);
    ::quote::__private::push_eq_spanned(&mut _s, _span);
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "core");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "option");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "Option");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_lt_spanned(&mut _s, _span);
    ::quote::ToTokens::to_tokens(&ty, &mut _s);
    ::quote::__private::push_gt_spanned(&mut _s, _span);
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "None");
    ::quote::__private::push_dot_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "unwrap");
    ::quote::__private::push_group_spanned(&mut _s, _span,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let _: ::quote::__private::Span =
                ::quote::__private::get_span(_span).__into_span();
            ::quote::__private::TokenStream::new()
        });
    ::quote::__private::push_semi_spanned(&mut _s, _span);
    _s
}quote_spanned! {lifetime.span()=>
167            ::core::compile_error!(
168                "this parameter borrows its object for the call and cannot be `'static`; \
169                 elide the lifetime, or take `ExternalPtr<T>` to keep a handle"
170            );
171            let #ident: #ty = ::core::option::Option::<#ty>::None.unwrap();
172        };
173    }
174    let value = replace_self(&arg.value, self_ty);
175    let span = arg.value.span();
176    let convert = if arg.optional {
177        {
    let mut _s = ::quote::__private::TokenStream::new();
    let _span: ::quote::__private::Span =
        ::quote::__private::get_span(span).__into_span();
    ::quote::__private::push_ident_spanned(&mut _s, _span,
        "__mx_optional_external_arg");
    _s
}quote_spanned! {span=> __mx_optional_external_arg }
178    } else {
179        {
    let mut _s = ::quote::__private::TokenStream::new();
    let _span: ::quote::__private::Span =
        ::quote::__private::get_span(span).__into_span();
    ::quote::__private::push_ident_spanned(&mut _s, _span,
        "__mx_external_arg");
    _s
}quote_spanned! {span=> __mx_external_arg }
180    };
181    let mutability = arg.mode.is_exclusive().then(|| {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "mut");
    _s
}quote! { mut });
182    // Spanned on `T`, like the borrows: every use of `T: TypedExternal` is a
183    // free function's bound there, so a `T` without it is one error.
184    {
    let mut _s = ::quote::__private::TokenStream::new();
    let _span: ::quote::__private::Span =
        ::quote::__private::get_span(span).__into_span();
    ::quote::__private::push_ident_spanned(&mut _s, _span, "let");
    ::quote::ToTokens::to_tokens(&mutability, &mut _s);
    ::quote::ToTokens::to_tokens(&handle, &mut _s);
    ::quote::__private::push_eq_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "match");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "miniextendr_api");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "from_r");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::ToTokens::to_tokens(&convert, &mut _s);
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_lt_spanned(&mut _s, _span);
    ::quote::ToTokens::to_tokens(&value, &mut _s);
    ::quote::__private::push_gt_spanned(&mut _s, _span);
    ::quote::__private::push_group_spanned(&mut _s, _span,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            let _: ::quote::__private::Span =
                ::quote::__private::get_span(_span).__into_span();
            ::quote::ToTokens::to_tokens(&sexp_ident, &mut _s);
            _s
        });
    ::quote::__private::push_group_spanned(&mut _s, _span,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            let _span: ::quote::__private::Span =
                ::quote::__private::get_span(_span).__into_span();
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "core");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "result");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "Result");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "Ok");
            ::quote::__private::push_group_spanned(&mut _s, _span,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    let _span: ::quote::__private::Span =
                        ::quote::__private::get_span(_span).__into_span();
                    ::quote::__private::push_ident_spanned(&mut _s, _span, "v");
                    _s
                });
            ::quote::__private::push_fat_arrow_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "v");
            ::quote::__private::push_comma_spanned(&mut _s, _span);
            ::quote::ToTokens::to_tokens(&err_arm, &mut _s);
            _s
        });
    ::quote::__private::push_semi_spanned(&mut _s, _span);
    _s
}quote_spanned! {span=>
185        let #mutability #handle = match ::miniextendr_api::from_r::#convert::<#value>(#sexp_ident) {
186            ::core::result::Result::Ok(v) => v,
187            #err_arm
188        };
189    }
190}
191
192/// The borrows of the borrowed external parameters of `inputs`, except those
193/// `unborrowed` names (a parameter marker rebinds them to another type).
194/// Each binds the parameter to its reference or view, holding the value's
195/// `RefCell` until the wrapper's closure ends, after return conversion.
196pub(crate) fn arg_borrows(
197    inputs: &syn::punctuated::Punctuated<syn::FnArg, ::syn::token::Commasyn::Token![,]>,
198    unborrowed: &[String],
199    self_ty: Option<&syn::Type>,
200) -> TokenStream {
201    let mut borrows = TokenStream::new();
202    for arg in inputs {
203        let syn::FnArg::Typed(pt) = arg else {
204            continue;
205        };
206        let syn::Pat::Ident(pat) = pt.pat.as_ref() else {
207            continue;
208        };
209        let Some(external) = external_arg(&pt.ty) else {
210            continue;
211        };
212        if unborrowed.contains(&crate::naming::ident_name(&pat.ident)) {
213            continue;
214        }
215        let name = &pat.ident;
216        let handle = handle_ident(name);
217        let guard = match ::quote::__private::IdentFragmentAdapter(&crate::naming::unraw(name)) {
    arg =>
        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                        ::alloc::fmt::format(format_args!("__mx_guard_{0}", arg))
                    }),
            ::quote::__private::Option::Some::<::quote::__private::Span>(proc_macro2::Span::mixed_site())),
}format_ident!(
218            "__mx_guard_{}",
219            crate::naming::unraw(name),
220            span = proc_macro2::Span::mixed_site()
221        );
222        let value = replace_self(&external.value, self_ty);
223        let span = external.value.span();
224        let api = {
    let mut _s = ::quote::__private::TokenStream::new();
    let _span: ::quote::__private::Span =
        ::quote::__private::get_span(span).__into_span();
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "miniextendr_api");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "externalptr");
    _s
}quote_spanned! {span=> ::miniextendr_api::externalptr };
225        borrows.extend(match (external.mode, external.optional) {
226            (Mode::Shared, false) => {
    let mut _s = ::quote::__private::TokenStream::new();
    let _span: ::quote::__private::Span =
        ::quote::__private::get_span(span).__into_span();
    ::quote::__private::push_ident_spanned(&mut _s, _span, "let");
    ::quote::ToTokens::to_tokens(&guard, &mut _s);
    ::quote::__private::push_eq_spanned(&mut _s, _span);
    ::quote::ToTokens::to_tokens(&api, &mut _s);
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "__mx_borrow");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_lt_spanned(&mut _s, _span);
    ::quote::ToTokens::to_tokens(&value, &mut _s);
    ::quote::__private::push_gt_spanned(&mut _s, _span);
    ::quote::__private::push_group_spanned(&mut _s, _span,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            let _span: ::quote::__private::Span =
                ::quote::__private::get_span(_span).__into_span();
            ::quote::__private::push_and_spanned(&mut _s, _span);
            ::quote::ToTokens::to_tokens(&handle, &mut _s);
            _s
        });
    ::quote::__private::push_semi_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "let");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::__private::push_eq_spanned(&mut _s, _span);
    ::quote::ToTokens::to_tokens(&api, &mut _s);
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "__mx_shared");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_lt_spanned(&mut _s, _span);
    ::quote::ToTokens::to_tokens(&value, &mut _s);
    ::quote::__private::push_gt_spanned(&mut _s, _span);
    ::quote::__private::push_group_spanned(&mut _s, _span,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            let _span: ::quote::__private::Span =
                ::quote::__private::get_span(_span).__into_span();
            ::quote::__private::push_and_spanned(&mut _s, _span);
            ::quote::ToTokens::to_tokens(&guard, &mut _s);
            _s
        });
    ::quote::__private::push_semi_spanned(&mut _s, _span);
    _s
}quote_spanned! {span=>
227                let #guard = #api::__mx_borrow::<#value>(&#handle);
228                let #name = #api::__mx_shared::<#value>(&#guard);
229            },
230            (Mode::Exclusive, false) => {
    let mut _s = ::quote::__private::TokenStream::new();
    let _span: ::quote::__private::Span =
        ::quote::__private::get_span(span).__into_span();
    ::quote::__private::push_ident_spanned(&mut _s, _span, "let");
    ::quote::__private::push_ident_spanned(&mut _s, _span, "mut");
    ::quote::ToTokens::to_tokens(&guard, &mut _s);
    ::quote::__private::push_eq_spanned(&mut _s, _span);
    ::quote::ToTokens::to_tokens(&api, &mut _s);
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "__mx_borrow_mut");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_lt_spanned(&mut _s, _span);
    ::quote::ToTokens::to_tokens(&value, &mut _s);
    ::quote::__private::push_gt_spanned(&mut _s, _span);
    ::quote::__private::push_group_spanned(&mut _s, _span,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            let _span: ::quote::__private::Span =
                ::quote::__private::get_span(_span).__into_span();
            ::quote::__private::push_and_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "mut");
            ::quote::ToTokens::to_tokens(&handle, &mut _s);
            _s
        });
    ::quote::__private::push_semi_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "let");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::__private::push_eq_spanned(&mut _s, _span);
    ::quote::ToTokens::to_tokens(&api, &mut _s);
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "__mx_exclusive");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_lt_spanned(&mut _s, _span);
    ::quote::ToTokens::to_tokens(&value, &mut _s);
    ::quote::__private::push_gt_spanned(&mut _s, _span);
    ::quote::__private::push_group_spanned(&mut _s, _span,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            let _span: ::quote::__private::Span =
                ::quote::__private::get_span(_span).__into_span();
            ::quote::__private::push_and_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "mut");
            ::quote::ToTokens::to_tokens(&guard, &mut _s);
            _s
        });
    ::quote::__private::push_semi_spanned(&mut _s, _span);
    _s
}quote_spanned! {span=>
231                let mut #guard = #api::__mx_borrow_mut::<#value>(&mut #handle);
232                let #name = #api::__mx_exclusive::<#value>(&mut #guard);
233            },
234            (Mode::SharedView, false) => {
    let mut _s = ::quote::__private::TokenStream::new();
    let _span: ::quote::__private::Span =
        ::quote::__private::get_span(span).__into_span();
    ::quote::__private::push_ident_spanned(&mut _s, _span, "let");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::__private::push_eq_spanned(&mut _s, _span);
    ::quote::ToTokens::to_tokens(&api, &mut _s);
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "__mx_borrow");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_lt_spanned(&mut _s, _span);
    ::quote::ToTokens::to_tokens(&value, &mut _s);
    ::quote::__private::push_gt_spanned(&mut _s, _span);
    ::quote::__private::push_group_spanned(&mut _s, _span,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            let _span: ::quote::__private::Span =
                ::quote::__private::get_span(_span).__into_span();
            ::quote::__private::push_and_spanned(&mut _s, _span);
            ::quote::ToTokens::to_tokens(&handle, &mut _s);
            _s
        });
    ::quote::__private::push_semi_spanned(&mut _s, _span);
    _s
}quote_spanned! {span=>
235                let #name = #api::__mx_borrow::<#value>(&#handle);
236            },
237            (Mode::ExclusiveView, false) => {
    let mut _s = ::quote::__private::TokenStream::new();
    let _span: ::quote::__private::Span =
        ::quote::__private::get_span(span).__into_span();
    ::quote::__private::push_ident_spanned(&mut _s, _span, "let");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::__private::push_eq_spanned(&mut _s, _span);
    ::quote::ToTokens::to_tokens(&api, &mut _s);
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "__mx_borrow_mut");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_lt_spanned(&mut _s, _span);
    ::quote::ToTokens::to_tokens(&value, &mut _s);
    ::quote::__private::push_gt_spanned(&mut _s, _span);
    ::quote::__private::push_group_spanned(&mut _s, _span,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            let _span: ::quote::__private::Span =
                ::quote::__private::get_span(_span).__into_span();
            ::quote::__private::push_and_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "mut");
            ::quote::ToTokens::to_tokens(&handle, &mut _s);
            _s
        });
    ::quote::__private::push_semi_spanned(&mut _s, _span);
    _s
}quote_spanned! {span=>
238                let #name = #api::__mx_borrow_mut::<#value>(&mut #handle);
239            },
240            (Mode::Shared, true) => {
    let mut _s = ::quote::__private::TokenStream::new();
    let _span: ::quote::__private::Span =
        ::quote::__private::get_span(span).__into_span();
    ::quote::__private::push_ident_spanned(&mut _s, _span, "let");
    ::quote::ToTokens::to_tokens(&guard, &mut _s);
    ::quote::__private::push_eq_spanned(&mut _s, _span);
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "core");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "option");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "Option");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "map");
    ::quote::__private::push_group_spanned(&mut _s, _span,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            let _span: ::quote::__private::Span =
                ::quote::__private::get_span(_span).__into_span();
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "core");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "option");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "Option");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "as_ref");
            ::quote::__private::push_group_spanned(&mut _s, _span,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    let _span: ::quote::__private::Span =
                        ::quote::__private::get_span(_span).__into_span();
                    ::quote::__private::push_and_spanned(&mut _s, _span);
                    ::quote::ToTokens::to_tokens(&handle, &mut _s);
                    _s
                });
            ::quote::__private::push_comma_spanned(&mut _s, _span);
            ::quote::ToTokens::to_tokens(&api, &mut _s);
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span,
                "__mx_borrow");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_lt_spanned(&mut _s, _span);
            ::quote::ToTokens::to_tokens(&value, &mut _s);
            ::quote::__private::push_gt_spanned(&mut _s, _span);
            ::quote::__private::push_comma_spanned(&mut _s, _span);
            _s
        });
    ::quote::__private::push_semi_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "let");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::__private::push_eq_spanned(&mut _s, _span);
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "core");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "option");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "Option");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "map");
    ::quote::__private::push_group_spanned(&mut _s, _span,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            let _span: ::quote::__private::Span =
                ::quote::__private::get_span(_span).__into_span();
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "core");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "option");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "Option");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "as_ref");
            ::quote::__private::push_group_spanned(&mut _s, _span,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    let _span: ::quote::__private::Span =
                        ::quote::__private::get_span(_span).__into_span();
                    ::quote::__private::push_and_spanned(&mut _s, _span);
                    ::quote::ToTokens::to_tokens(&guard, &mut _s);
                    _s
                });
            ::quote::__private::push_comma_spanned(&mut _s, _span);
            ::quote::ToTokens::to_tokens(&api, &mut _s);
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span,
                "__mx_shared");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_lt_spanned(&mut _s, _span);
            ::quote::ToTokens::to_tokens(&value, &mut _s);
            ::quote::__private::push_gt_spanned(&mut _s, _span);
            ::quote::__private::push_comma_spanned(&mut _s, _span);
            _s
        });
    ::quote::__private::push_semi_spanned(&mut _s, _span);
    _s
}quote_spanned! {span=>
241                let #guard = ::core::option::Option::map(
242                    ::core::option::Option::as_ref(&#handle), #api::__mx_borrow::<#value>,
243                );
244                let #name = ::core::option::Option::map(
245                    ::core::option::Option::as_ref(&#guard), #api::__mx_shared::<#value>,
246                );
247            },
248            (Mode::Exclusive, true) => {
    let mut _s = ::quote::__private::TokenStream::new();
    let _span: ::quote::__private::Span =
        ::quote::__private::get_span(span).__into_span();
    ::quote::__private::push_ident_spanned(&mut _s, _span, "let");
    ::quote::__private::push_ident_spanned(&mut _s, _span, "mut");
    ::quote::ToTokens::to_tokens(&guard, &mut _s);
    ::quote::__private::push_eq_spanned(&mut _s, _span);
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "core");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "option");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "Option");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "map");
    ::quote::__private::push_group_spanned(&mut _s, _span,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            let _span: ::quote::__private::Span =
                ::quote::__private::get_span(_span).__into_span();
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "core");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "option");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "Option");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "as_mut");
            ::quote::__private::push_group_spanned(&mut _s, _span,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    let _span: ::quote::__private::Span =
                        ::quote::__private::get_span(_span).__into_span();
                    ::quote::__private::push_and_spanned(&mut _s, _span);
                    ::quote::__private::push_ident_spanned(&mut _s, _span,
                        "mut");
                    ::quote::ToTokens::to_tokens(&handle, &mut _s);
                    _s
                });
            ::quote::__private::push_comma_spanned(&mut _s, _span);
            ::quote::ToTokens::to_tokens(&api, &mut _s);
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span,
                "__mx_borrow_mut");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_lt_spanned(&mut _s, _span);
            ::quote::ToTokens::to_tokens(&value, &mut _s);
            ::quote::__private::push_gt_spanned(&mut _s, _span);
            ::quote::__private::push_comma_spanned(&mut _s, _span);
            _s
        });
    ::quote::__private::push_semi_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "let");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::__private::push_eq_spanned(&mut _s, _span);
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "core");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "option");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "Option");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "map");
    ::quote::__private::push_group_spanned(&mut _s, _span,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            let _span: ::quote::__private::Span =
                ::quote::__private::get_span(_span).__into_span();
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "core");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "option");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "Option");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "as_mut");
            ::quote::__private::push_group_spanned(&mut _s, _span,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    let _span: ::quote::__private::Span =
                        ::quote::__private::get_span(_span).__into_span();
                    ::quote::__private::push_and_spanned(&mut _s, _span);
                    ::quote::__private::push_ident_spanned(&mut _s, _span,
                        "mut");
                    ::quote::ToTokens::to_tokens(&guard, &mut _s);
                    _s
                });
            ::quote::__private::push_comma_spanned(&mut _s, _span);
            ::quote::ToTokens::to_tokens(&api, &mut _s);
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span,
                "__mx_exclusive");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_lt_spanned(&mut _s, _span);
            ::quote::ToTokens::to_tokens(&value, &mut _s);
            ::quote::__private::push_gt_spanned(&mut _s, _span);
            ::quote::__private::push_comma_spanned(&mut _s, _span);
            _s
        });
    ::quote::__private::push_semi_spanned(&mut _s, _span);
    _s
}quote_spanned! {span=>
249                let mut #guard = ::core::option::Option::map(
250                    ::core::option::Option::as_mut(&mut #handle), #api::__mx_borrow_mut::<#value>,
251                );
252                let #name = ::core::option::Option::map(
253                    ::core::option::Option::as_mut(&mut #guard), #api::__mx_exclusive::<#value>,
254                );
255            },
256            (Mode::SharedView, true) => {
    let mut _s = ::quote::__private::TokenStream::new();
    let _span: ::quote::__private::Span =
        ::quote::__private::get_span(span).__into_span();
    ::quote::__private::push_ident_spanned(&mut _s, _span, "let");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::__private::push_eq_spanned(&mut _s, _span);
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "core");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "option");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "Option");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "map");
    ::quote::__private::push_group_spanned(&mut _s, _span,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            let _span: ::quote::__private::Span =
                ::quote::__private::get_span(_span).__into_span();
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "core");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "option");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "Option");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "as_ref");
            ::quote::__private::push_group_spanned(&mut _s, _span,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    let _span: ::quote::__private::Span =
                        ::quote::__private::get_span(_span).__into_span();
                    ::quote::__private::push_and_spanned(&mut _s, _span);
                    ::quote::ToTokens::to_tokens(&handle, &mut _s);
                    _s
                });
            ::quote::__private::push_comma_spanned(&mut _s, _span);
            ::quote::ToTokens::to_tokens(&api, &mut _s);
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span,
                "__mx_borrow");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_lt_spanned(&mut _s, _span);
            ::quote::ToTokens::to_tokens(&value, &mut _s);
            ::quote::__private::push_gt_spanned(&mut _s, _span);
            ::quote::__private::push_comma_spanned(&mut _s, _span);
            _s
        });
    ::quote::__private::push_semi_spanned(&mut _s, _span);
    _s
}quote_spanned! {span=>
257                let #name = ::core::option::Option::map(
258                    ::core::option::Option::as_ref(&#handle), #api::__mx_borrow::<#value>,
259                );
260            },
261            (Mode::ExclusiveView, true) => {
    let mut _s = ::quote::__private::TokenStream::new();
    let _span: ::quote::__private::Span =
        ::quote::__private::get_span(span).__into_span();
    ::quote::__private::push_ident_spanned(&mut _s, _span, "let");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::__private::push_eq_spanned(&mut _s, _span);
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "core");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "option");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "Option");
    ::quote::__private::push_colon2_spanned(&mut _s, _span);
    ::quote::__private::push_ident_spanned(&mut _s, _span, "map");
    ::quote::__private::push_group_spanned(&mut _s, _span,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            let _span: ::quote::__private::Span =
                ::quote::__private::get_span(_span).__into_span();
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "core");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "option");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "Option");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span, "as_mut");
            ::quote::__private::push_group_spanned(&mut _s, _span,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    let _span: ::quote::__private::Span =
                        ::quote::__private::get_span(_span).__into_span();
                    ::quote::__private::push_and_spanned(&mut _s, _span);
                    ::quote::__private::push_ident_spanned(&mut _s, _span,
                        "mut");
                    ::quote::ToTokens::to_tokens(&handle, &mut _s);
                    _s
                });
            ::quote::__private::push_comma_spanned(&mut _s, _span);
            ::quote::ToTokens::to_tokens(&api, &mut _s);
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_ident_spanned(&mut _s, _span,
                "__mx_borrow_mut");
            ::quote::__private::push_colon2_spanned(&mut _s, _span);
            ::quote::__private::push_lt_spanned(&mut _s, _span);
            ::quote::ToTokens::to_tokens(&value, &mut _s);
            ::quote::__private::push_gt_spanned(&mut _s, _span);
            ::quote::__private::push_comma_spanned(&mut _s, _span);
            _s
        });
    ::quote::__private::push_semi_spanned(&mut _s, _span);
    _s
}quote_spanned! {span=>
262                let #name = ::core::option::Option::map(
263                    ::core::option::Option::as_mut(&mut #handle), #api::__mx_borrow_mut::<#value>,
264                );
265            },
266        });
267    }
268    borrows
269}
270
271#[cfg(test)]
272mod tests {
273    use super::*;
274
275    fn arg(ty: syn::Type) -> Option<(String, bool, Mode)> {
276        external_arg(&ty).map(|arg| {
277            let value = &arg.value;
278            (quote!(#value).to_string(), arg.optional, arg.mode)
279        })
280    }
281
282    #[test]
283    fn references_to_types_without_conversions_and_views_are_external() {
284        let some = |value: &str, optional, mode| Some((value.to_owned(), optional, mode));
285        assert_eq!(
286            arg(syn::parse_quote!(&Counter)),
287            some("Counter", false, Mode::Shared)
288        );
289        assert_eq!(
290            arg(syn::parse_quote!(&mut crate::Counter)),
291            some("crate :: Counter", false, Mode::Exclusive)
292        );
293        assert_eq!(
294            arg(syn::parse_quote!(&'a Self)),
295            some("Self", false, Mode::Shared)
296        );
297        assert_eq!(
298            arg(syn::parse_quote!(Option<&mut Counter>)),
299            some("Counter", true, Mode::Exclusive)
300        );
301        assert_eq!(
302            arg(syn::parse_quote!(ExternalPtrRef<'_, Counter>)),
303            some("Counter", false, Mode::SharedView)
304        );
305        assert_eq!(
306            arg(syn::parse_quote!(
307                Option<::miniextendr_api::ExternalPtrMut<'a, Counter>>
308            )),
309            some("Counter", true, Mode::ExclusiveView)
310        );
311    }
312
313    #[test]
314    fn native_string_dots_container_and_handle_types_are_not_external() {
315        for ty in [
316            quote!(&str),
317            quote!(&Path),
318            quote!(&i32),
319            quote!(&mut f64),
320            quote!(&RLogical),
321            quote!(&[i32]),
322            quote!(&mut [f64]),
323            quote!(&Dots),
324            quote!(&String),
325            quote!(&Vec<i32>),
326            quote!(&Option<Counter>),
327            quote!(&ExternalPtr<Counter>),
328            quote!(ExternalPtr<Counter>),
329            quote!(Option<ExternalPtr<Counter>>),
330            quote!(Vec<&Counter>),
331            quote!(Counter),
332        ] {
333            let ty: syn::Type = syn::parse2(ty).unwrap();
334            assert!(external_arg(&ty).is_none(), "{}", quote!(#ty));
335        }
336    }
337
338    #[test]
339    fn borrows_follow_the_mode() {
340        let sig: syn::Signature = syn::parse_quote!(fn f(
341            a: &mut Counter,
342            b: Option<&Counter>,
343            n: i32,
344            c: ExternalPtr<Counter>,
345            d: ExternalPtrRef<'_, Counter>,
346            e: Option<ExternalPtrMut<'_, Counter>>,
347            m: &Counter,
348        ));
349        let borrow = arg_borrows(&sig.inputs, &["m".to_owned()], None).to_string();
350        assert!(
351            borrow.contains("let a = :: miniextendr_api :: externalptr :: __mx_exclusive :: < Counter > (& mut __mx_guard_a)"),
352            "{borrow}"
353        );
354        assert!(borrow.contains("__mx_shared"), "{borrow}");
355        assert!(
356            borrow.contains("let d = :: miniextendr_api :: externalptr :: __mx_borrow :: < Counter > (& __mx_ptr_d)"),
357            "{borrow}"
358        );
359        assert!(
360            borrow.contains("let e = :: core :: option :: Option :: map"),
361            "{borrow}"
362        );
363        assert_eq!(borrow.matches("__mx_borrow_mut").count(), 2, "{borrow}");
364        assert!(
365            !borrow.contains("__mx_ptr_c") && !borrow.contains("__mx_ptr_n"),
366            "{borrow}"
367        );
368        assert!(!borrow.contains("__mx_ptr_m"), "{borrow}");
369    }
370
371    #[test]
372    fn handle_conversion_names_the_concrete_type_and_mutability() {
373        let ident: syn::Ident = syn::parse_quote!(other);
374        let sexp: syn::Ident = syn::parse_quote!(arg_0);
375        let ty: syn::Type = syn::parse_quote!(&mut Self);
376        let arg = external_arg(&ty).unwrap();
377        let self_ty: syn::Type = syn::parse_quote!(Counter);
378        let stmt =
379            handle_conversion(&ident, &sexp, &ty, &arg, Some(&self_ty), quote! {}).to_string();
380        assert!(stmt.contains("let mut __mx_ptr_other"), "{stmt}");
381        assert!(
382            stmt.contains("__mx_external_arg :: < Counter > (arg_0)"),
383            "{stmt}"
384        );
385
386        let ty: syn::Type = syn::parse_quote!(&'static Counter);
387        let arg = external_arg(&ty).unwrap();
388        let stmt = handle_conversion(&ident, &sexp, &ty, &arg, None, quote! {}).to_string();
389        assert!(stmt.contains("compile_error"), "{stmt}");
390    }
391}