use crate::generics::{GenericParam, Generics, TypeParamBound, WherePredicate};
use proc_macro2::{Span, TokenStream};
use proc_macro_error::{emit_call_site_error, emit_error};
use quote::{quote, ToTokens, TokenStreamExt};
use std::{iter, slice};
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::token::{Colon2, Comma, Eq};
use syn::{parse_quote, Attribute, FnArg, Ident, Item, Token, TraitItem, Type, TypePath};
pub struct ForDeref {
generics: Generics,
definitive: Ident,
targets: Punctuated<Type, Comma>,
}
trait FilterAttrs<'a> {
type Ret: Iterator<Item = &'a Attribute>;
fn outer(self) -> Self::Ret;
}
impl<'a> FilterAttrs<'a> for &'a [Attribute] {
type Ret = iter::Filter<slice::Iter<'a, Attribute>, fn(&&Attribute) -> bool>;
fn outer(self) -> Self::Ret {
fn is_outer(attr: &&Attribute) -> bool {
match attr.style {
syn::AttrStyle::Outer => true,
syn::AttrStyle::Inner(_) => false,
}
}
self.iter().filter(is_outer)
}
}
mod parsing {
use super::*;
use syn::parse::{Error, Parse, ParseStream, Result};
impl Parse for ForDeref {
fn parse(input: ParseStream) -> Result<Self> {
let _ = input.parse::<Token![for]>()?;
let mut generics: Generics = input.parse()?;
let targets = Punctuated::parse_separated_nonempty(input)?;
let mut lookahead = input.lookahead1();
if lookahead.peek(Token![where]) {
generics.where_clause = Some(input.parse()?);
lookahead = input.lookahead1();
}
if !input.is_empty() {
return Err(lookahead.error());
}
let mut definitive: Option<Ident> = None;
for param in &generics.params {
if let GenericParam::Type(param) = param {
for bound in ¶m.bounds {
if matches!(bound, TypeParamBound::TraitSubst(_)) {
definitive = Some(param.ident.clone());
break;
}
}
}
}
if definitive.is_none() {
if let Some(clause) = generics.where_clause.as_ref() {
for pred in &clause.predicates {
if let WherePredicate::Type(pred) = pred {
for bound in &pred.bounds {
if matches!(bound, TypeParamBound::TraitSubst(_)) {
if let Type::Path(TypePath { qself: None, path }) =
&pred.bounded_ty
{
if let Some(ident) = path.get_ident() {
definitive = Some(ident.clone());
break;
}
}
}
}
}
}
}
}
let definitive = match definitive {
Some(def) => def,
None => {
return Err(Error::new(
Span::call_site(),
"no definitive type parameter: require a parameter bound like `T: trait``",
));
}
};
Ok(ForDeref {
generics,
definitive,
targets,
})
}
}
}
fn has_bound_on_self(gen: &syn::Generics) -> bool {
if let Some(ref clause) = gen.where_clause {
for pred in clause.predicates.iter() {
if let syn::WherePredicate::Type(ref ty) = pred {
if let Type::Path(ref bounded) = ty.bounded_ty {
if bounded.qself.is_none() && bounded.path.is_ident("Self") {
if ty
.bounds
.iter()
.any(|bound| matches!(bound, syn::TypeParamBound::Trait(_)))
{
return true;
}
}
}
}
}
}
false
}
impl ForDeref {
pub fn expand(self, item: TokenStream) -> TokenStream {
let trait_def = match syn::parse2::<Item>(item) {
Ok(Item::Trait(item)) => item,
Ok(item) => {
emit_error!(item, "expected trait");
return TokenStream::new();
}
Err(err) => {
emit_error!(err);
return TokenStream::new();
}
};
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
enum Bound {
None,
Deref(bool), ErrorEmitted,
}
let mut bound = Bound::None;
let trait_ident = &trait_def.ident;
let (_, trait_generics, _) = trait_def.generics.split_for_impl();
let trait_ty = quote! { #trait_ident #trait_generics };
let ty_generics = self.generics.ty_generics(&trait_def.generics);
let (impl_generics, where_clause) =
self.generics.impl_generics(&trait_def.generics, &trait_ty);
let definitive_ty = self.definitive;
let definitive = quote! { < #definitive_ty as #trait_ty > };
let mut impl_items = TokenStream::new();
let tokens = &mut impl_items;
for item in &trait_def.items {
match item {
TraitItem::Const(item) => {
for attr in item.attrs.iter() {
if attr.path == parse_quote! { cfg } {
attr.to_tokens(tokens);
}
}
item.const_token.to_tokens(tokens);
item.ident.to_tokens(tokens);
item.colon_token.to_tokens(tokens);
item.ty.to_tokens(tokens);
Eq::default().to_tokens(tokens);
definitive.to_tokens(tokens);
Colon2::default().to_tokens(tokens);
item.ident.to_tokens(tokens);
item.semi_token.to_tokens(tokens);
}
TraitItem::Method(item) => {
for attr in item.attrs.iter() {
if attr.path == parse_quote! { cfg } {
attr.to_tokens(tokens);
}
}
if has_bound_on_self(&item.sig.generics) {
if item.default.is_none() {
emit_call_site_error!(
"cannot autoimpl trait with Deref";
note = item.span() => "method has a bound on Self and no default implementation";
);
}
continue;
}
item.sig.to_tokens(tokens);
bound = bound.max(match item.sig.inputs.first() {
Some(FnArg::Receiver(rec)) => {
if rec.reference.is_some() {
Bound::Deref(rec.mutability.is_some())
} else {
emit_call_site_error!(
"cannot autoimpl trait with Deref";
note = rec.span() => "deref cannot yield `self` by value";
);
Bound::ErrorEmitted
}
}
Some(FnArg::Typed(ref pat)) => match &*pat.ty {
Type::Reference(rf) if rf.elem == parse_quote! { Self } => {
Bound::Deref(rf.mutability.is_some())
}
_ => Bound::None,
},
_ => Bound::None,
});
let ident = &item.sig.ident;
let params = item.sig.inputs.iter().map(|arg| match arg {
FnArg::Receiver(arg) => &arg.self_token as &dyn ToTokens,
FnArg::Typed(arg) => &arg.pat,
});
tokens.append_all(quote! { {
#definitive :: #ident ( #(#params),* )
} });
}
TraitItem::Type(item) => {
for attr in item.attrs.iter() {
if attr.path == parse_quote! { cfg } {
attr.to_tokens(tokens);
}
}
if has_bound_on_self(&item.generics) {
emit_call_site_error!(
"cannot autoimpl trait with Deref";
note = item.span() => "type has a bound on Self";
);
}
item.type_token.to_tokens(tokens);
item.ident.to_tokens(tokens);
let (_, ty_generics, where_clause) = item.generics.split_for_impl();
ty_generics.to_tokens(tokens);
Eq::default().to_tokens(tokens);
definitive.to_tokens(tokens);
Colon2::default().to_tokens(tokens);
item.ident.to_tokens(tokens);
ty_generics.to_tokens(tokens);
where_clause.to_tokens(tokens);
item.semi_token.to_tokens(tokens);
}
TraitItem::Macro(item) => {
emit_error!(item, "unsupported: macro item in trait");
}
TraitItem::Verbatim(item) => {
emit_error!(item, "unsupported: verbatim item in trait");
}
_ => (),
}
}
let mut toks = TokenStream::new();
match bound {
Bound::None => (),
Bound::Deref(is_mut) => {
let bound = match is_mut {
false => quote! { ::core::ops::Deref },
true => quote! { ::core::ops::DerefMut },
};
let target_impls = self.targets.iter().map(|target| {
quote! {
impl #impl_generics TargetMustImplDeref #ty_generics for #target
#where_clause {}
}
});
toks.append_all(quote! {
#[automatically_derived]
const _: () = {
trait TargetMustImplDeref #impl_generics: #bound<Target = #definitive_ty>
#where_clause {}
#(#target_impls)*
};
});
}
Bound::ErrorEmitted => return toks,
}
for target in self.targets {
toks.append_all(quote! {
#[automatically_derived]
impl #impl_generics #trait_ty for #target #where_clause {
#impl_items
}
});
}
toks
}
}