auto_impl/gen.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914
use proc_macro2::{Span as Span2, TokenStream as TokenStream2};
use proc_macro_error::{abort, emit_error};
use quote::{ToTokens, TokenStreamExt};
use syn::{
punctuated::Punctuated, spanned::Spanned, Attribute, FnArg, GenericParam, Ident, ItemTrait,
Lifetime, Pat, PatIdent, PatType, ReturnType, Signature, Token, TraitBound, TraitBoundModifier,
TraitItem, TraitItemConst, TraitItemMethod, TraitItemType, Type, TypeParamBound,
WherePredicate,
};
use crate::{
analyze::find_suitable_param_names,
attr::{is_our_attr, parse_our_attr, OurAttr},
proxy::ProxyType,
};
/// Generates one complete impl of the given trait for each of the given proxy
/// types. All impls are returned as token stream.
pub(crate) fn gen_impls(proxy_types: &[ProxyType], trait_def: &syn::ItemTrait) -> TokenStream2 {
let mut tokens = TokenStream2::new();
let (proxy_ty_param, proxy_lt_param) = find_suitable_param_names(trait_def);
// One impl for each proxy type
for proxy_type in proxy_types {
let header = gen_header(proxy_type, trait_def, &proxy_ty_param, &proxy_lt_param);
let items = gen_items(proxy_type, trait_def, &proxy_ty_param);
if let ProxyType::Box | ProxyType::Rc | ProxyType::Arc = proxy_type {
tokens.append_all(quote! {
const _: () = {
extern crate alloc;
#header { #( #items )* }
};
});
} else {
tokens.append_all(quote! {
const _: () = {
#header { #( #items )* }
};
});
}
}
tokens
}
/// Generates the header of the impl of the given trait for the given proxy
/// type.
fn gen_header(
proxy_type: &ProxyType,
trait_def: &ItemTrait,
proxy_ty_param: &Ident,
proxy_lt_param: &Lifetime,
) -> TokenStream2 {
// Generate generics for impl positions from trait generics.
let (impl_generics, trait_generics, where_clause) = trait_def.generics.split_for_impl();
// The name of the trait with all generic parameters applied.
let trait_ident = &trait_def.ident;
let trait_path = quote! { #trait_ident #trait_generics };
// Here we assemble the parameter list of the impl (the thing in
// `impl< ... >`). This is simply the parameter list of the trait with
// one or two parameters added. For a trait `trait Foo<'x, 'y, A, B>`,
// it will look like this:
//
// '{proxy_lt_param}, 'x, 'y, A, B, {proxy_ty_param}
//
// The `'{proxy_lt_param}` in the beginning is only added when the proxy
// type is `&` or `&mut`.
let impl_generics = {
// Determine whether we can add a `?Sized` relaxation to allow trait
// objects. We can do that as long as there is no method that has a
// `self` by value receiver and no `where Self: Sized` bound.
let sized_required = trait_def
.items
.iter()
// Only interested in methods
.filter_map(|item| {
if let TraitItem::Method(m) = item {
Some(m)
} else {
None
}
})
// We also ignore methods that we will not override. In the case of
// invalid attributes it is save to assume default behavior.
.filter(|m| !should_keep_default_for(m, proxy_type))
.any(|m| {
// Check if there is a `Self: Sized` bound on the method.
let self_is_bounded_sized = m
.sig
.generics
.where_clause
.iter()
.flat_map(|wc| &wc.predicates)
.filter_map(|pred| {
if let WherePredicate::Type(p) = pred {
Some(p)
} else {
None
}
})
.any(|pred| {
// Check if the type is `Self`
match &pred.bounded_ty {
Type::Path(p) if p.path.is_ident("Self") => {
// Check if the bound contains `Sized`
pred.bounds.iter().any(|b| match b {
TypeParamBound::Trait(TraitBound {
modifier: TraitBoundModifier::None,
path,
..
}) => path.is_ident("Sized"),
_ => false,
})
}
_ => false,
}
});
// Check if the first parameter is `self` by value. In that
// case, we might require `Self` to be `Sized`.
let self_value_param = match m.sig.inputs.first() {
Some(FnArg::Receiver(receiver)) => receiver.reference.is_none(),
_ => false,
};
// Check if return type is `Self`
let self_value_return = match &m.sig.output {
ReturnType::Type(_, t) => {
if let Type::Path(p) = &**t {
p.path.is_ident("Self")
} else {
false
}
}
_ => false,
};
// TODO: check for `Self` parameter in any other argument.
// If for this method, `Self` is used in a position that
// requires `Self: Sized` or this bound is added explicitly, we
// cannot add the `?Sized` relaxation to the impl body.
self_value_param || self_value_return || self_is_bounded_sized
});
let relaxation = if sized_required {
quote! {}
} else {
quote! { + ?::core::marker::Sized }
};
// Check if there are some `Self: Foo` bounds on methods. If so, we
// need to add those bounds to `T` as well. See issue #11 for more
// information, but in short: there is no better solution. Method where
// clauses with `Self: Foo` force us to add `T: Foo` to the impl
// header, as we otherwise cannot generate a valid impl block.
let additional_bounds = trait_def
.items
.iter()
// Only interested in methods
.filter_map(|item| {
if let TraitItem::Method(m) = item {
Some(m)
} else {
None
}
})
// We also ignore methods that we will not override. In the case of
// invalid attributes it is save to assume default behavior.
.filter(|m| !should_keep_default_for(m, proxy_type))
// Exact all relevant bounds
.flat_map(|m| {
m.sig
.generics
.where_clause
.iter()
.flat_map(|wc| &wc.predicates)
.filter_map(|pred| {
if let WherePredicate::Type(p) = pred {
Some(p)
} else {
None
}
})
.filter(|p| {
// Only `Self:` bounds
match &p.bounded_ty {
Type::Path(p) => p.path.is_ident("Self"),
_ => false,
}
})
.flat_map(|p| &p.bounds)
.filter_map(|b| {
// We are only interested in trait bounds. That's
// because lifetime bounds on `Self` do not need to be
// added to the impl header. That's because all values
// "derived" from `self` also meet the same lifetime
// bound as `self`. In simpler terms: while `self.field`
// might not be `Clone` even if `Self: Clone`,
// `self.field` is always `: 'a` if `Self: 'a`.
match b {
TypeParamBound::Trait(t) => Some(t),
TypeParamBound::Lifetime(_) => None,
}
})
});
// Determine if our proxy type needs a lifetime parameter
let (mut params, ty_bounds) = match proxy_type {
ProxyType::Ref | ProxyType::RefMut => (
quote! { #proxy_lt_param, },
quote! { : #proxy_lt_param + #trait_path #relaxation #(+ #additional_bounds)* },
),
ProxyType::Box | ProxyType::Rc | ProxyType::Arc => (
quote! {},
quote! { : #trait_path #relaxation #(+ #additional_bounds)* },
),
ProxyType::Fn | ProxyType::FnMut | ProxyType::FnOnce => {
let fn_bound = gen_fn_type_for_trait(proxy_type, trait_def);
(quote! {}, quote! { : #fn_bound })
}
};
// Append all parameters from the trait. Sadly, `impl_generics`
// includes the angle brackets `< >` so we have to remove them like
// this.
let mut tts = impl_generics
.into_token_stream()
.into_iter()
.skip(1) // the opening `<`
.collect::<Vec<_>>();
tts.pop(); // the closing `>`
params.append_all(&tts);
// Append proxy type parameter (if there aren't any parameters so far,
// we need to add a comma first).
let comma = if params.is_empty() || tts.is_empty() {
quote! {}
} else {
quote! { , }
};
params.append_all(quote! { #comma #proxy_ty_param #ty_bounds });
params
};
// The tokens after `for` in the impl header (the type the trait is
// implemented for).
#[rustfmt::skip]
let self_ty = match *proxy_type {
ProxyType::Ref => quote! { & #proxy_lt_param #proxy_ty_param },
ProxyType::RefMut => quote! { & #proxy_lt_param mut #proxy_ty_param },
ProxyType::Arc => quote! { alloc::sync::Arc<#proxy_ty_param> },
ProxyType::Rc => quote! { alloc::rc::Rc<#proxy_ty_param> },
ProxyType::Box => quote! { alloc::boxed::Box<#proxy_ty_param> },
ProxyType::Fn => quote! { #proxy_ty_param },
ProxyType::FnMut => quote! { #proxy_ty_param },
ProxyType::FnOnce => quote! { #proxy_ty_param },
};
// If the trait has super traits, we need to add the super trait bound to
// our self type. This can only be done in the where clause, so we need to
// combine the existing where clauses with our new predicate in that case.
let where_clause = if !trait_def.supertraits.is_empty() {
let mut out = quote! { where };
if !trait_def.supertraits.is_empty() {
let supertraits = &trait_def.supertraits;
out.extend(quote! { #self_ty: #supertraits, });
}
if let Some(predicates) = where_clause.map(|c| &c.predicates) {
out.extend(predicates.into_token_stream());
}
out
} else {
where_clause.into_token_stream()
};
// Combine everything
quote! {
impl<#impl_generics> #trait_path for #self_ty #where_clause
}
}
/// Generates the Fn-trait type (e.g. `FnMut(u32) -> String`) for the given
/// trait and proxy type (the latter has to be `Fn`, `FnMut` or `FnOnce`!)
///
/// If the trait is unsuitable to be implemented for the given proxy type, an
/// error is emitted.
fn gen_fn_type_for_trait(proxy_type: &ProxyType, trait_def: &ItemTrait) -> TokenStream2 {
// Only traits with exactly one method can be implemented for Fn-traits.
// Associated types and consts are also not allowed.
let method = trait_def.items.get(0).and_then(|item| {
if let TraitItem::Method(m) = item {
Some(m)
} else {
None
}
});
// If this requirement is not satisfied, we emit an error.
if method.is_none() || trait_def.items.len() > 1 {
emit_error!(
trait_def.span(),
"this trait cannot be auto-implemented for Fn-traits (only traits with exactly \
one method and no other items are allowed)",
);
return TokenStream2::new();
}
// We checked for `None` above
let method = method.unwrap();
let sig = &method.sig;
// Check for forbidden modifier of the method
if let Some(const_token) = sig.constness {
emit_error!(
const_token.span(),
"the trait '{}' cannot be auto-implemented for Fn-traits: const methods are not \
allowed",
trait_def.ident,
);
}
if let Some(unsafe_token) = &sig.unsafety {
emit_error!(
unsafe_token.span(),
"the trait '{}' cannot be auto-implemented for Fn-traits: unsafe methods are not \
allowed",
trait_def.ident,
);
}
if let Some(abi_token) = &sig.abi {
emit_error!(
abi_token.span(),
"the trait '{}' cannot be implemented for Fn-traits: custom ABIs are not allowed",
trait_def.ident,
);
}
// Function traits cannot support generics in their arguments
// These would require HRTB for types instead of just lifetimes
for type_param in sig.generics.type_params() {
emit_error!(
type_param.span(),
"the trait '{}' cannot be implemented for Fn-traits: generic arguments are not allowed",
trait_def.ident,
);
}
for const_param in sig.generics.const_params() {
emit_error!(
const_param.span(),
"the trait '{}' cannot be implemented for Fn-traits: constant arguments are not allowed",
trait_def.ident,
);
}
// =======================================================================
// Check if the trait can be implemented for the given proxy type
let self_type = SelfType::from_sig(sig);
let err = match (self_type, proxy_type) {
// The method needs to have a receiver
(SelfType::None, _) => Some(("Fn-traits", "no", "")),
// We can't impl methods with `&mut self` or `&self` receiver for
// `FnOnce`
(SelfType::Mut, ProxyType::FnOnce) => {
Some(("`FnOnce`", "a `&mut self`", " (only `self` is allowed)"))
}
(SelfType::Ref, ProxyType::FnOnce) => {
Some(("`FnOnce`", "a `&self`", " (only `self` is allowed)"))
}
// We can't impl methods with `&self` receiver for `FnMut`
(SelfType::Ref, ProxyType::FnMut) => Some((
"`FnMut`",
"a `&self`",
" (only `self` and `&mut self` are allowed)",
)),
// Other combinations are fine
_ => None,
};
if let Some((fn_traits, receiver, allowed)) = err {
emit_error!(
method.sig.span(),
"the trait '{}' cannot be auto-implemented for {}, because this method has \
{} receiver{}",
trait_def.ident,
fn_traits,
receiver,
allowed,
);
return TokenStream2::new();
}
// =======================================================================
// Generate the full Fn-type
// The path to the Fn-trait
let fn_name = match proxy_type {
ProxyType::Fn => quote! { ::core::ops::Fn },
ProxyType::FnMut => quote! { ::core::ops::FnMut },
ProxyType::FnOnce => quote! { ::core::ops::FnOnce },
_ => panic!("internal error in auto_impl (function contract violation)"),
};
// The return type
let ret = &sig.output;
// Now it gets a bit complicated. The types of the function signature
// could contain "local" lifetimes, meaning that they are not declared in
// the trait definition (or are `'static`). We need to extract all local
// lifetimes to declare them with HRTB (e.g. `for<'a>`).
//
// In Rust 2015 that was easy: we could just take the lifetimes explicitly
// declared in the function signature. Those were the local lifetimes.
// Unfortunately, with in-band lifetimes, things get more complicated. We
// need to take a look at all lifetimes inside the types (arbitrarily deep)
// and check if they are local or not.
//
// In cases where lifetimes are omitted (e.g. `&str`), we don't have a
// problem. If we just translate that to `for<> Fn(&str)`, it's fine: all
// omitted lifetimes in an `Fn()` type are automatically declared as HRTB.
//
// TODO: Implement this check for in-band lifetimes!
let local_lifetimes = sig.generics.lifetimes();
// The input types as comma separated list. We skip the first argument, as
// this is the receiver argument.
let mut arg_types = TokenStream2::new();
for arg in sig.inputs.iter().skip(1) {
match arg {
FnArg::Typed(pat) => {
let ty = &pat.ty;
arg_types.append_all(quote! { #ty , });
}
// We skipped the receiver already.
FnArg::Receiver(r) => {
abort!(
r.span(),
"receiver argument that's not the first argument (auto_impl is confused)",
);
}
}
}
quote! {
for< #(#local_lifetimes),* > #fn_name (#arg_types) #ret
}
}
/// Generates the implementation of all items of the given trait. These
/// implementations together are the body of the `impl` block.
fn gen_items(
proxy_type: &ProxyType,
trait_def: &ItemTrait,
proxy_ty_param: &Ident,
) -> Vec<TokenStream2> {
trait_def
.items
.iter()
.filter_map(|item| {
match item {
TraitItem::Const(c) => {
gen_const_item(proxy_type, c, trait_def, proxy_ty_param).ok()
}
TraitItem::Method(method) => {
gen_method_item(proxy_type, method, trait_def, proxy_ty_param).ok()
}
TraitItem::Type(ty) => {
gen_type_item(proxy_type, ty, trait_def, proxy_ty_param).ok()
}
TraitItem::Macro(mac) => {
// We cannot resolve the macro invocation and thus cannot know
// if it adds additional items to the trait. Thus, we have to
// give up.
emit_error!(
mac.span(),
"traits with macro invocations in their bodies are not \
supported by auto_impl",
);
None
}
TraitItem::Verbatim(v) => {
// I don't quite know when this happens, but it's better to
// notify the user with a nice error instead of panicking.
emit_error!(
v.span(),
"unexpected 'verbatim'-item (auto-impl doesn't know how to handle it)",
);
None
}
_ => {
// `syn` enums are `non_exhaustive` to be future-proof. If a
// trait contains a kind of item we don't even know about, we
// emit an error.
emit_error!(
item.span(),
"unknown trait item (auto-impl doesn't know how to handle it)",
);
None
}
}
})
.collect()
}
/// Generates the implementation of an associated const item described by
/// `item`. The implementation is returned as token stream.
///
/// If the proxy type is an Fn*-trait, an error is emitted and `Err(())` is
/// returned.
fn gen_const_item(
proxy_type: &ProxyType,
item: &TraitItemConst,
trait_def: &ItemTrait,
proxy_ty_param: &Ident,
) -> Result<TokenStream2, ()> {
// A trait with associated consts cannot be implemented for Fn* types.
if proxy_type.is_fn() {
emit_error!(
item.span(),
"the trait `{}` cannot be auto-implemented for Fn-traits, because it has \
associated consts",
trait_def.ident;
note = "only traits with a single method can be implemented for Fn-traits";
);
return Err(());
}
// We simply use the associated const from our type parameter.
let const_name = &item.ident;
let const_ty = &item.ty;
let attrs = filter_attrs(&item.attrs);
Ok(quote! {
#(#attrs)* const #const_name: #const_ty = #proxy_ty_param::#const_name;
})
}
/// Generates the implementation of an associated type item described by `item`.
/// The implementation is returned as token stream.
///
/// If the proxy type is an Fn*-trait, an error is emitted and `Err(())` is
/// returned.
fn gen_type_item(
proxy_type: &ProxyType,
item: &TraitItemType,
trait_def: &ItemTrait,
proxy_ty_param: &Ident,
) -> Result<TokenStream2, ()> {
// A trait with associated types cannot be implemented for Fn* types.
if proxy_type.is_fn() {
emit_error!(
item.span(),
"the trait `{}` cannot be auto-implemented for Fn-traits, because it has \
associated types",
trait_def.ident;
note = "only traits with a single method can be implemented for Fn-traits";
);
return Err(());
}
// We simply use the associated type from our type parameter.
let assoc_name = &item.ident;
let attrs = filter_attrs(&item.attrs);
Ok(quote! {
#(#attrs)* type #assoc_name = #proxy_ty_param::#assoc_name;
})
}
/// Generates the implementation of a method item described by `item`. The
/// implementation is returned as token stream.
///
/// This function also performs sanity checks, e.g. whether the proxy type can
/// be used to implement the method. If any error occurs, the error is
/// immediately emitted and `Err(())` is returned.
fn gen_method_item(
proxy_type: &ProxyType,
item: &TraitItemMethod,
trait_def: &ItemTrait,
proxy_ty_param: &Ident,
) -> Result<TokenStream2, ()> {
// If this method has a `#[auto_impl(keep_default_for(...))]` attribute for
// the given proxy type, we don't generate anything for this impl block.
if should_keep_default_for(item, proxy_type) {
return if item.default.is_some() {
Ok(TokenStream2::new())
} else {
emit_error!(
item.sig.span(),
"the method `{}` has the attribute `keep_default_for` but is not a default \
method (no body is provided)",
item.sig.ident,
);
Err(())
};
}
// Determine the kind of the method, determined by the self type.
let sig = &item.sig;
let self_arg = SelfType::from_sig(sig);
let attrs = filter_attrs(&item.attrs);
// Check self type and proxy type combination
check_receiver_compatible(proxy_type, self_arg, &trait_def.ident, sig.span());
// Generate the list of argument used to call the method.
let (inputs, args) = get_arg_list(sig.inputs.iter());
// Construct a signature we'll use to generate the proxy method impl
// This is _almost_ the same as the original, except we use the inputs constructed
// alongside the args. These may be slightly different than the original trait.
let sig = Signature {
constness: item.sig.constness,
asyncness: item.sig.asyncness,
unsafety: item.sig.unsafety,
abi: item.sig.abi.clone(),
fn_token: item.sig.fn_token,
ident: item.sig.ident.clone(),
generics: item.sig.generics.clone(),
paren_token: item.sig.paren_token,
inputs,
variadic: item.sig.variadic.clone(),
output: item.sig.output.clone(),
};
// Build the turbofish type parameters. We need to pass type parameters
// explicitly as they cannot be inferred in all cases (e.g. something like
// `mem::size_of`). However, we don't explicitly specify lifetime
// parameters. Most lifetime parameters are so called late-bound lifetimes
// (ones that stick to input parameters) and Rust prohibits us from
// specifying late-bound lifetimes explicitly (which is not a problem,
// because those can always be correctly inferred). It would be possible to
// explicitly specify early-bound lifetimes, but this is hardly useful.
// Early-bound lifetimes are lifetimes that are only attached to the return
// type. Something like:
//
// fn foo<'a>() -> &'a i32
//
// It's hard to imagine how such a function would even work. So since those
// functions are really rare and special, we won't support them. In
// particular, for us to determine if a lifetime parameter is early- or
// late-bound would be *really* difficult.
//
// So we just specify type parameters. In the future, however, we need to
// add support for const parameters. But those are not remotely stable yet,
// so we can wait a bit still.
let generic_types = sig
.generics
.params
.iter()
.filter_map(|param| match param {
GenericParam::Type(param) => {
let name = ¶m.ident;
Some(quote! { #name , })
}
GenericParam::Const(param) => {
let name = ¶m.ident;
Some(quote! { #name , })
}
GenericParam::Lifetime(_) => None,
})
.collect::<TokenStream2>();
let generic_types = if generic_types.is_empty() {
generic_types
} else {
quote! { ::<#generic_types> }
};
// Generate the body of the function. This mainly depends on the self type,
// but also on the proxy type.
let fn_name = &sig.ident;
let body = match self_arg {
// Fn proxy types get a special treatment
_ if proxy_type.is_fn() => {
quote! { ({self})(#args) }
}
// No receiver
SelfType::None => {
// The proxy type is a reference, smart pointer or Box.
quote! { #proxy_ty_param::#fn_name #generic_types(#args) }
}
// Receiver `self` (by value)
SelfType::Value => {
// The proxy type is a Box.
quote! { #proxy_ty_param::#fn_name #generic_types(*self, #args) }
}
// `&self` or `&mut self` receiver
SelfType::Ref | SelfType::Mut => {
// The proxy type could be anything in the `Ref` case, and `&mut`
// or Box in the `Mut` case.
quote! { #proxy_ty_param::#fn_name #generic_types(self, #args) }
}
};
// Combine body with signature
Ok(quote! { #(#attrs)* #sig { #body }})
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum SelfType {
None,
Ref,
Mut,
Value,
}
impl SelfType {
fn from_sig(sig: &Signature) -> Self {
match sig.inputs.iter().next() {
Some(FnArg::Receiver(r)) => {
if r.reference.is_none() {
SelfType::Value
} else if r.mutability.is_none() {
SelfType::Ref
} else {
SelfType::Mut
}
}
_ => SelfType::None,
}
}
fn as_str(&self) -> Option<&'static str> {
match *self {
SelfType::None => None,
SelfType::Ref => Some("&self"),
SelfType::Mut => Some("&mut self"),
SelfType::Value => Some("self"),
}
}
}
/// Checks if this method can be implemented for the given proxy type. If not,
/// we will emit an error pointing to the method signature.
fn check_receiver_compatible(
proxy_type: &ProxyType,
self_arg: SelfType,
trait_name: &Ident,
sig_span: Span2,
) {
match (proxy_type, self_arg) {
(ProxyType::Ref, SelfType::Mut) | (ProxyType::Ref, SelfType::Value) => {
emit_error!(
sig_span,
"the trait `{}` cannot be auto-implemented for immutable references, because \
this method has a `{}` receiver",
trait_name,
self_arg.as_str().unwrap();
note = "only `&self` and no receiver are allowed";
);
}
(ProxyType::RefMut, SelfType::Value) => {
emit_error!(
sig_span,
"the trait `{}` cannot be auto-implemented for mutable references, because \
this method has a `self` receiver",
trait_name;
note = "only `&self`, `&mut self` and no receiver are allowed";
);
}
(ProxyType::Rc, SelfType::Mut)
| (ProxyType::Rc, SelfType::Value)
| (ProxyType::Arc, SelfType::Mut)
| (ProxyType::Arc, SelfType::Value) => {
let ptr_name = if *proxy_type == ProxyType::Rc {
"Rc"
} else {
"Arc"
};
emit_error!(
sig_span,
"the trait `{}` cannot be auto-implemented for {}, because \
this method has a `{}` receiver",
trait_name,
ptr_name,
self_arg.as_str().unwrap();
note = "only `&self` and no receiver are allowed";
);
}
(ProxyType::Fn, _) | (ProxyType::FnMut, _) | (ProxyType::FnOnce, _) => {
// The Fn-trait being compatible with the receiver was already
// checked before (in `gen_fn_type_for_trait()`).
}
_ => {} // All other combinations are fine
}
}
/// Generates a list of comma-separated arguments used to call the function.
/// Currently, only simple names are valid and more complex pattern will lead
/// to an error being emitted. `self` parameters are ignored.
fn get_arg_list<'a>(
original_inputs: impl Iterator<Item = &'a FnArg>,
) -> (Punctuated<FnArg, Token![,]>, TokenStream2) {
let mut args = TokenStream2::new();
let mut inputs = Punctuated::new();
for arg in original_inputs {
match arg {
FnArg::Typed(arg) => {
// Make sure the argument pattern is a simple name. In
// principle, we could probably support patterns, but it's
// not that important now.
if let Pat::Ident(PatIdent {
by_ref: _by_ref,
mutability: _mutability,
ident,
subpat: None,
attrs,
}) = &*arg.pat
{
// Add name plus trailing comma to tokens
args.append_all(quote! { #ident , });
// Add an input argument that omits the `mut` and `ref` pattern bindings
inputs.push(FnArg::Typed(PatType {
attrs: arg.attrs.clone(),
pat: Box::new(Pat::Ident(PatIdent {
attrs: attrs.clone(),
by_ref: None,
mutability: None,
ident: ident.clone(),
subpat: None,
})),
colon_token: arg.colon_token,
ty: arg.ty.clone(),
}))
} else {
emit_error!(
arg.pat.span(), "argument patterns are not supported by #[auto-impl]";
help = "please use a simple name like \"foo\" (but not `_`)";
);
continue;
}
}
// There is only one such argument. We handle it elsewhere and
// can ignore it here.
FnArg::Receiver(arg) => {
inputs.push(FnArg::Receiver(arg.clone()));
}
}
}
(inputs, args)
}
/// Checks if the given method has the attribute `#[auto_impl(keep_default_for(...))]`
/// and if it contains the given proxy type.
fn should_keep_default_for(m: &TraitItemMethod, proxy_type: &ProxyType) -> bool {
// Get an iterator of just the attribute we are interested in.
let mut it = m
.attrs
.iter()
.filter(|attr| is_our_attr(attr))
.filter_map(|attr| parse_our_attr(attr).ok());
// Check the first (and hopefully only) `keep_default_for` attribute.
let out = match it.next() {
Some(attr) => {
// Check if the attribute lists the given proxy type.
let OurAttr::KeepDefaultFor(proxy_types) = attr;
proxy_types.contains(proxy_type)
}
// If there is no such attribute, we return `false`
None => false,
};
// Check if there is another such attribute (which we disallow)
if it.next().is_some() {
emit_error!(
m.sig.span(),
"found two `keep_default_for` attributes on one method",
);
}
out
}
fn filter_attrs(attrs: &[Attribute]) -> Vec<Attribute> {
attrs
.iter()
.filter(|attr| attr.path.is_ident("cfg"))
.cloned()
.collect()
}