glib_macros/
clone.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use proc_macro2::{Ident, Span, TokenStream};
4use quote::{ToTokens, quote};
5use syn::{
6    Attribute, Expr, ExprAsync, ExprClosure, Token,
7    parse::{Parse, ParseStream},
8    spanned::Spanned,
9};
10
11use crate::utils::crate_ident_new;
12
13#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14pub(crate) enum CaptureKind {
15    Watch,
16    Weak,
17    WeakAllowNone,
18    Strong,
19    ToOwned,
20}
21
22impl TryFrom<&'_ Ident> for CaptureKind {
23    type Error = syn::Error;
24
25    fn try_from(s: &Ident) -> Result<Self, Self::Error> {
26        Ok(match s.to_string().as_str() {
27            "watch" => CaptureKind::Watch,
28            "strong" => CaptureKind::Strong,
29            "weak" => CaptureKind::Weak,
30            "weak_allow_none" => CaptureKind::WeakAllowNone,
31            "to_owned" => CaptureKind::ToOwned,
32            _ => {
33                // This is actually never shown to the user but we need some kind of error type for
34                // TryFrom, () would be enough but then clippy complains.
35                //
36                // We'll keep it here in case it is useful somewhere at a later time.
37                return Err(syn::Error::new(
38                    s.span(),
39                    format!("unknown capture type `{s}`"),
40                ));
41            }
42        })
43    }
44}
45
46#[derive(Default)]
47pub(crate) enum UpgradeBehaviour {
48    #[default]
49    Unit,
50    Panic,
51    Default,
52    Expression(Expr),
53    Closure(ExprClosure),
54}
55
56impl UpgradeBehaviour {
57    pub(crate) fn maybe_parse(
58        attrs: &[Attribute],
59        input: ParseStream,
60    ) -> syn::Result<Option<Self>> {
61        // Caller checked for empty
62        let attr = &attrs[0];
63        attr.meta.require_path_only()?;
64
65        let Some(attr_name) = attr.path().get_ident() else {
66            return Ok(None);
67        };
68
69        let upgrade_behaviour = match attr_name.to_string().as_str() {
70            "upgrade_or" => {
71                let expr = input.parse::<Expr>()?;
72                input.parse::<Token![,]>()?;
73                UpgradeBehaviour::Expression(expr)
74            }
75            "upgrade_or_else" => {
76                let closure = input.parse::<ExprClosure>()?;
77                if closure.asyncness.is_some() {
78                    return Err(syn::Error::new_spanned(
79                        &closure,
80                        "`upgrade_or_else` closure needs to be a non-async closure",
81                    ));
82                }
83                if !closure.inputs.is_empty() {
84                    return Err(syn::Error::new_spanned(
85                        &closure,
86                        "`upgrade_or_else` closure must not have any parameters",
87                    ));
88                }
89
90                input.parse::<Token![,]>()?;
91                UpgradeBehaviour::Closure(closure)
92            }
93            "upgrade_or_default" => UpgradeBehaviour::Default,
94            "upgrade_or_panic" => UpgradeBehaviour::Panic,
95            _ => {
96                return Ok(None);
97            }
98        };
99
100        if attrs.len() > 1 {
101            return Err(syn::Error::new_spanned(
102                &attrs[1],
103                format!(
104                    "upgrade failure attribute must not be followed by any other attributes. Found {} more attribute{}",
105                    attrs.len() - 1,
106                    if attrs.len() > 2 { "s" } else { "" },
107                ),
108            ));
109        }
110
111        let next_attrs = &input.call(Attribute::parse_outer)?;
112        if !next_attrs.is_empty() {
113            return Err(syn::Error::new_spanned(
114                &next_attrs[0],
115                format!(
116                    "upgrade failure attribute must not be followed by any other attributes. Found {} more attribute{}",
117                    next_attrs.len(),
118                    if next_attrs.len() > 1 { "s" } else { "" },
119                ),
120            ));
121        }
122
123        Ok(Some(upgrade_behaviour))
124    }
125}
126
127pub(crate) struct Capture {
128    pub(crate) name: Expr,
129    pub(crate) alias: Option<Ident>,
130    pub(crate) kind: CaptureKind,
131}
132
133impl Capture {
134    pub(crate) fn maybe_parse(
135        attrs: &[Attribute],
136        input: ParseStream,
137    ) -> syn::Result<Option<Self>> {
138        // Caller checked for empty
139        let attr = &attrs[0];
140
141        let Some(attr_name) = attr.path().get_ident() else {
142            return Ok(None);
143        };
144        let Ok(kind) = CaptureKind::try_from(attr_name) else {
145            return Ok(None);
146        };
147
148        if attrs.len() > 1 {
149            return Err(syn::Error::new_spanned(
150                &attrs[1],
151                "variable capture attributes must be followed by an identifier",
152            ));
153        }
154
155        let mut alias = None;
156        if let syn::Meta::List(ref list) = attr.meta {
157            list.parse_nested_meta(|meta| {
158                if meta.path.is_ident("rename_to") {
159                    let value = meta.value()?;
160                    let id = value.parse::<Ident>()?;
161                    if alias.is_some() {
162                        return Err(meta.error("multiple `rename_to` properties are not allowed"));
163                    }
164                    alias = Some(id);
165                } else if let Some(ident) = meta.path.get_ident() {
166                    return Err(
167                        meta.error(
168                            format!(
169                                "unsupported capture attribute property `{ident}`: only `rename_to` is supported"
170                            ),
171                        ),
172                    );
173                } else {
174                    return Err(meta.error("unsupported capture attribute property"));
175                }
176                Ok(())
177            })?;
178        }
179
180        let name = input.parse::<Expr>()?;
181        match name {
182            Expr::Path(ref p) if p.path.get_ident().is_some() => {
183                if p.path.get_ident().unwrap() == "self" && alias.is_none() {
184                    return Err(syn::Error::new_spanned(
185                        attr,
186                        "capture attribute for `self` requires usage of the `rename_to` attribute property",
187                    ));
188                }
189                // Nothing to do, it's just an identifier
190            }
191            _ if alias.is_some() => {
192                // Nothing to do, it's an alias
193            }
194            _ => {
195                return Err(syn::Error::new_spanned(
196                    attr,
197                    "capture attribute for an expression requires usage of the `rename_to` attribute property",
198                ));
199            }
200        }
201
202        input.parse::<Token![,]>()?;
203
204        Ok(Some(Capture { name, alias, kind }))
205    }
206
207    pub(crate) fn alias(&self) -> TokenStream {
208        if let Some(ref alias) = self.alias {
209            alias.to_token_stream()
210        } else {
211            self.name.to_token_stream()
212        }
213    }
214
215    pub(crate) fn outer_before_tokens(&self, crate_ident: &TokenStream) -> TokenStream {
216        let alias = self.alias();
217        let name = &self.name;
218        match self.kind {
219            CaptureKind::Watch => quote! {
220                let #alias = #crate_ident::object::Watchable::watched_object(&#name);
221            },
222            CaptureKind::Weak | CaptureKind::WeakAllowNone => quote! {
223                let #alias = #crate_ident::clone::Downgrade::downgrade(&#name);
224            },
225            CaptureKind::Strong => quote! {
226                let #alias = #name.clone();
227            },
228            CaptureKind::ToOwned => quote! {
229                let #alias = ::std::borrow::ToOwned::to_owned(&*#name);
230            },
231        }
232    }
233
234    pub(crate) fn outer_after_tokens(
235        &self,
236        crate_ident: &TokenStream,
237        closure_ident: &Ident,
238    ) -> TokenStream {
239        let name = &self.name;
240        match self.kind {
241            CaptureKind::Watch => quote! {
242                #crate_ident::object::Watchable::watch_closure(&#name, &#closure_ident);
243            },
244            _ => Default::default(),
245        }
246    }
247
248    pub(crate) fn inner_before_tokens(
249        &self,
250        crate_ident: &TokenStream,
251        weak_upgrade_failure_kind: &UpgradeBehaviour,
252        upgrade_failure_closure_ident: &Ident,
253        unit_return: Option<TokenStream>,
254    ) -> TokenStream {
255        let alias = self.alias();
256        match self.kind {
257            CaptureKind::Watch => {
258                quote! {
259                    let #alias = unsafe { #alias.borrow() };
260                    let #alias = ::core::convert::AsRef::as_ref(&#alias);
261                }
262            }
263            CaptureKind::Weak => match weak_upgrade_failure_kind {
264                UpgradeBehaviour::Panic => {
265                    let err_msg = format!(
266                        "Failed to upgrade `{alias}`. If you don't want to panic, use `#[upgrade_or]`, `#[upgrade_or_else]` or `#[upgrade_or_default]`",
267                    );
268                    quote! {
269                        let Some(#alias) = #crate_ident::clone::Upgrade::upgrade(&#alias) else {
270                            panic!(#err_msg);
271                        };
272                    }
273                }
274                UpgradeBehaviour::Default
275                | UpgradeBehaviour::Expression(_)
276                | UpgradeBehaviour::Closure(_) => {
277                    let err_msg = format!("Failed to upgrade `{alias}`");
278                    quote! {
279                        let Some(#alias) = #crate_ident::clone::Upgrade::upgrade(&#alias) else {
280                            #crate_ident::g_debug!(
281                                #crate_ident::CLONE_MACRO_LOG_DOMAIN,
282                                #err_msg,
283                            );
284                            return (#upgrade_failure_closure_ident)();
285                        };
286                    }
287                }
288                UpgradeBehaviour::Unit => {
289                    let err_msg = format!("Failed to upgrade `{alias}`");
290                    let unit_return = unit_return.unwrap_or_else(|| {
291                        quote! { return; }
292                    });
293                    quote! {
294                        let Some(#alias) = #crate_ident::clone::Upgrade::upgrade(&#alias) else {
295                            #crate_ident::g_debug!(
296                                #crate_ident::CLONE_MACRO_LOG_DOMAIN,
297                                #err_msg,
298                            );
299                            #unit_return
300                        };
301                    }
302                }
303            },
304            CaptureKind::WeakAllowNone => quote! {
305                let #alias = #crate_ident::clone::Upgrade::upgrade(&#alias);
306            },
307            _ => Default::default(),
308        }
309    }
310}
311
312#[derive(Clone)]
313enum ClosureOrAsync {
314    Closure(ExprClosure),
315    Async(ExprAsync),
316}
317
318impl Parse for ClosureOrAsync {
319    fn parse(input: ParseStream) -> syn::Result<Self> {
320        let expr = input.parse::<Expr>()?;
321        match expr {
322            Expr::Async(async_) => {
323                if async_.capture.is_none() {
324                    return Err(syn::Error::new_spanned(
325                        async_,
326                        "async blocks need to capture variables by move. Please add the `move` keyword",
327                    ));
328                }
329
330                Ok(ClosureOrAsync::Async(async_))
331            }
332            Expr::Closure(closure) => {
333                if closure.capture.is_none() {
334                    return Err(syn::Error::new_spanned(
335                        closure,
336                        "closures need to capture variables by move. Please add the `move` keyword",
337                    ));
338                }
339
340                Ok(ClosureOrAsync::Closure(closure))
341            }
342            _ => Err(syn::Error::new_spanned(
343                expr,
344                "only closures and async blocks are supported",
345            )),
346        }
347    }
348}
349
350impl ToTokens for ClosureOrAsync {
351    fn to_tokens(&self, tokens: &mut TokenStream) {
352        match self {
353            ClosureOrAsync::Closure(c) => c.to_tokens(tokens),
354            ClosureOrAsync::Async(a) => a.to_tokens(tokens),
355        }
356    }
357}
358
359struct Clone {
360    captures: Vec<Capture>,
361    upgrade_behaviour: UpgradeBehaviour,
362    body: ClosureOrAsync,
363}
364
365impl Parse for Clone {
366    fn parse(input: ParseStream) -> syn::Result<Self> {
367        if input.is_empty() {
368            return Err(syn::Error::new(
369                Span::call_site(),
370                "expected a closure or async block",
371            ));
372        }
373
374        let mut captures: Vec<Capture> = vec![];
375        let mut upgrade_behaviour: Option<(UpgradeBehaviour, Span)> = None;
376
377        loop {
378            // There must either be one or no attributes here. Multiple attributes are not
379            // supported.
380            //
381            // If this is a capture attribute, it must be followed by an identifier.
382            // If this is an upgrade failure attribute, it might be followed by a closure. After the
383            // upgrade failure attribute there must not be any further attributes.
384            //
385            // If this is not an attribute then it is a closure, async closure or async block which
386            // is handled outside the loop
387            let attrs = input.call(Attribute::parse_outer)?;
388            if attrs.is_empty() {
389                break;
390            };
391
392            match Capture::maybe_parse(&attrs, input)? {
393                Some(capture) => {
394                    if capture.kind == CaptureKind::Watch {
395                        return Err(syn::Error::new_spanned(
396                            &attrs[0],
397                            "watch variable captures are not supported",
398                        ));
399                    }
400
401                    captures.push(capture);
402                }
403                _ => match UpgradeBehaviour::maybe_parse(&attrs, input)? {
404                    Some(behaviour) => {
405                        if upgrade_behaviour.is_some() {
406                            return Err(syn::Error::new_spanned(
407                                &attrs[0],
408                                "multiple upgrade failure attributes are not supported",
409                            ));
410                        }
411
412                        upgrade_behaviour = Some((behaviour, attrs[0].span()));
413                        break;
414                    }
415                    _ => {
416                        if let Some(ident) = attrs[0].path().get_ident() {
417                            return Err(syn::Error::new_spanned(
418                                &attrs[0],
419                                format!(
420                                    "unsupported attribute `{ident}`: only `strong`, `weak`, `weak_allow_none`, `to_owned`, `upgrade_or`, `upgrade_or_else`, `upgrade_or_default` and `upgrade_or_panic` are supported",
421                                ),
422                            ));
423                        } else {
424                            return Err(syn::Error::new_spanned(
425                                &attrs[0],
426                                "unsupported attribute: only `strong`, `weak`, `weak_allow_none`, `to_owned`, `upgrade_or_else`, `upgrade_or_default` and `upgrade_or_panic` are supported",
427                            ));
428                        }
429                    }
430                },
431            }
432        }
433
434        if let Some((_, ref span)) = upgrade_behaviour
435            && captures.iter().all(|c| c.kind != CaptureKind::Weak)
436        {
437            return Err(syn::Error::new(
438                *span,
439                "upgrade failure attribute can only be used together with weak variable captures",
440            ));
441        }
442
443        let upgrade_behaviour = upgrade_behaviour.map(|x| x.0).unwrap_or_default();
444
445        // Following is a closure or async block
446        let body = input.parse::<ClosureOrAsync>()?;
447
448        // Trailing comma, if any
449        if input.peek(Token![,]) {
450            input.parse::<Token![,]>()?;
451        }
452
453        Ok(Clone {
454            captures,
455            upgrade_behaviour,
456            body,
457        })
458    }
459}
460
461impl ToTokens for Clone {
462    fn to_tokens(&self, tokens: &mut TokenStream) {
463        let crate_ident = crate_ident_new();
464
465        let upgrade_failure_closure_ident =
466            Ident::new("____upgrade_failure_closure", Span::call_site());
467
468        let outer_before = self
469            .captures
470            .iter()
471            .map(|c| c.outer_before_tokens(&crate_ident));
472        let inner_before = self.captures.iter().map(|c| {
473            c.inner_before_tokens(
474                &crate_ident,
475                &self.upgrade_behaviour,
476                &upgrade_failure_closure_ident,
477                None,
478            )
479        });
480
481        let upgrade_failure_closure = match self.upgrade_behaviour {
482            UpgradeBehaviour::Default => Some(quote! {
483                let #upgrade_failure_closure_ident = ::std::default::Default::default;
484            }),
485            UpgradeBehaviour::Expression(ref expr) => Some(quote! {
486                let #upgrade_failure_closure_ident = move || {
487                    #expr
488                };
489            }),
490            UpgradeBehaviour::Closure(ref closure) => Some(quote! {
491                let #upgrade_failure_closure_ident = #closure;
492            }),
493            _ => None,
494        };
495
496        let body = match self.body {
497            ClosureOrAsync::Closure(ref c) => {
498                let ExprClosure {
499                    attrs,
500                    lifetimes,
501                    constness,
502                    movability,
503                    asyncness,
504                    capture,
505                    or1_token,
506                    inputs,
507                    or2_token,
508                    output,
509                    body,
510                } = c;
511
512                quote! {
513                    #(#attrs)*
514                    #lifetimes
515                    #constness
516                    #movability
517                    #asyncness
518                    #capture
519                    #or1_token
520                    #inputs
521                    #or2_token
522                    #output
523                    {
524                        #upgrade_failure_closure
525                        #(#inner_before)*
526                        #body
527                    }
528                }
529            }
530            ClosureOrAsync::Async(ref a) => {
531                let ExprAsync {
532                    attrs,
533                    async_token,
534                    capture,
535                    block,
536                } = a;
537
538                // Directly output the statements instead of the whole block including braces as we
539                // already produce a block with braces below and otherwise a compiler warning about
540                // unnecessary braces is wrongly emitted.
541                let stmts = &block.stmts;
542
543                quote! {
544                    #(#attrs)*
545                    #async_token
546                    #capture
547                    {
548                        #upgrade_failure_closure
549                        #(#inner_before)*
550                        #(#stmts)*
551                    }
552                }
553            }
554        };
555
556        tokens.extend(quote! {
557            {
558                #(#outer_before)*
559                #body
560            }
561        });
562    }
563}
564
565pub(crate) fn clone_inner(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
566    let clone = syn::parse_macro_input!(input as Clone);
567    clone.into_token_stream().into()
568}