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