| 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 | |