1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use proc_macro2::{Ident, Span, TokenStream}; |
4 | use quote::{quote, ToTokens}; |
5 | use syn::{ |
6 | parse::{Parse, ParseStream}, |
7 | spanned::Spanned, |
8 | Attribute, Expr, ExprAsync, ExprClosure, Token, |
9 | }; |
10 | |
11 | use crate::utils::crate_ident_new; |
12 | |
13 | #[derive (Clone, Copy, Debug, Eq, PartialEq)] |
14 | pub(crate) enum CaptureKind { |
15 | Watch, |
16 | Weak, |
17 | WeakAllowNone, |
18 | Strong, |
19 | ToOwned, |
20 | } |
21 | |
22 | impl 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 | message:format!("unknown capture type ` {s}`" ), |
40 | )); |
41 | } |
42 | }) |
43 | } |
44 | } |
45 | |
46 | #[derive (Default)] |
47 | pub(crate) enum UpgradeBehaviour { |
48 | #[default] |
49 | Unit, |
50 | Panic, |
51 | Default, |
52 | Expression(Expr), |
53 | Closure(ExprClosure), |
54 | } |
55 | |
56 | impl 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 | |
126 | pub(crate) struct Capture { |
127 | pub(crate) name: Expr, |
128 | pub(crate) alias: Option<Ident>, |
129 | pub(crate) kind: CaptureKind, |
130 | } |
131 | |
132 | impl 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)] |
317 | enum ClosureOrAsync { |
318 | Closure(ExprClosure), |
319 | Async(ExprAsync), |
320 | } |
321 | |
322 | impl 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 | |
354 | impl ToTokens for ClosureOrAsync { |
355 | fn to_tokens(&self, tokens: &mut TokenStream) { |
356 | match self { |
357 | ClosureOrAsync::Closure(ref c: &ExprClosure) => c.to_tokens(tokens), |
358 | ClosureOrAsync::Async(ref a: &ExprAsync) => a.to_tokens(tokens), |
359 | } |
360 | } |
361 | } |
362 | |
363 | struct Clone { |
364 | captures: Vec<Capture>, |
365 | upgrade_behaviour: UpgradeBehaviour, |
366 | body: ClosureOrAsync, |
367 | } |
368 | |
369 | impl 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 | |
450 | impl 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 | |
554 | pub(crate) fn clone_inner(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
555 | let clone: Clone = syn::parse_macro_input!(input as Clone); |
556 | clone.into_token_stream().into() |
557 | } |
558 | |