diff options
Diffstat (limited to 'src/parse_trait_sig.rs')
-rw-r--r-- | src/parse_trait_sig.rs | 361 |
1 files changed, 0 insertions, 361 deletions
diff --git a/src/parse_trait_sig.rs b/src/parse_trait_sig.rs deleted file mode 100644 index 1267707..0000000 --- a/src/parse_trait_sig.rs +++ /dev/null @@ -1,361 +0,0 @@ -use std::collections::HashMap; - -use proc_macro2::Span; -use syn::{ - spanned::Spanned, FnArg, PredicateType, Receiver, ReturnType, Type, TypePath, WherePredicate, -}; -use syn::{Ident, Signature, TypeImplTrait}; - -use crate::match_assoc_type; -use crate::parse_assoc_type::BoxType; -use crate::syn_utils::{iter_type, trait_bounds, TypeOrPath}; -use crate::transform::{dynamize_function_bounds, TransformError, TypeConverter}; - -#[derive(Debug, Clone)] -pub enum TypeTransform { - NoOp, - Into, - Box(BoxType), - Map(Box<TypeTransform>), - Tuple(Vec<TypeTransform>), - IntoIterMapCollect(Vec<TypeTransform>), - Iterator(BoxType, Box<TypeTransform>), - Result(Box<TypeTransform>, Box<TypeTransform>), -} - -#[derive(Debug)] -pub enum MethodError { - NonDispatchableMethod, - AssocTypeInInputs, - ImplTraitInInputs, - - Transform(TransformError), - - UnconvertedAssocType, -} - -impl From<TransformError> for MethodError { - fn from(err: TransformError) -> Self { - Self::Transform(err) - } -} - -fn filter_map_impl_trait(item: TypeOrPath) -> Option<&TypeImplTrait> { - match item { - TypeOrPath::Type(Type::ImplTrait(impltrait)) => Some(impltrait), - _other => None, - } -} - -pub struct SignatureChanges { - pub return_type: TypeTransform, - pub type_param_transforms: HashMap<Ident, Vec<TypeTransform>>, -} - -pub fn parse_trait_signature( - signature: &mut Signature, - type_converter: &TypeConverter, -) -> Result<SignatureChanges, (Span, MethodError)> { - if is_non_dispatchable(signature) { - return Err((signature.span(), MethodError::NonDispatchableMethod)); - } - - // provide better error messages for associated types in params - for input in &signature.inputs { - if let FnArg::Typed(pattype) = input { - if iter_type(&pattype.ty).any(match_assoc_type) { - return Err((pattype.ty.span(), MethodError::AssocTypeInInputs)); - } - if let Some(impl_trait) = iter_type(&pattype.ty).find_map(filter_map_impl_trait) { - return Err((impl_trait.span(), MethodError::ImplTraitInInputs)); - } - } - } - - let type_param_transforms = dynamize_function_bounds(&mut signature.generics, type_converter)?; - - let return_type = match &mut signature.output { - ReturnType::Type(_, og_type) => match type_converter.convert_type(og_type) { - Ok(ret_type) => ret_type, - Err((span, err)) => { - return Err((span, err.into())); - } - }, - ReturnType::Default => TypeTransform::NoOp, - }; - Ok(SignatureChanges { - return_type, - type_param_transforms, - }) -} - -fn is_non_dispatchable(signature: &Signature) -> bool { - // non-dispatchable: fn example(&self) where Self: Sized; - if let Some(where_clause) = &signature.generics.where_clause { - if where_clause - .predicates - .iter() - .any(bounds_self_and_has_bound_sized) - { - return true; - } - } - - // non-dispatchable: fn example(); - if signature.inputs.is_empty() { - return true; - } - - // non-dispatchable: fn example(arg: Type); - if matches!(signature.inputs.first(), Some(FnArg::Typed(_))) { - return true; - } - - // non-dispatchable: fn example(self); - if matches!( - signature.inputs.first(), - Some(FnArg::Receiver(Receiver { - reference: None, - .. - })) - ) { - return true; - } - false -} - -/// Returns true if the bounded type is `Self` and the bounds contain `Sized`. -fn bounds_self_and_has_bound_sized(predicate: &WherePredicate) -> bool { - matches!( - predicate, - WherePredicate::Type(PredicateType { - bounded_ty: Type::Path(TypePath { path, .. }), - bounds, - .. - }) - if path.is_ident("Self") - && trait_bounds(bounds).any(|b| b.path.is_ident("Sized")) - ) -} - -#[cfg(test)] -mod tests { - use quote::{format_ident, quote}; - use syn::{TraitItemMethod, Type}; - - use crate::{ - parse_assoc_type::DestType, - parse_trait_sig::{parse_trait_signature, MethodError, SignatureChanges, TypeTransform}, - transform::{TransformError, TypeConverter}, - }; - - #[test] - fn ok_void() { - let mut type1: TraitItemMethod = syn::parse2(quote! { - fn test(&self); - }) - .unwrap(); - - assert!(matches!( - parse_trait_signature(&mut type1.sig, &Default::default()), - Ok(SignatureChanges { - return_type: TypeTransform::NoOp, - .. - }) - )); - } - - #[test] - fn ok_assoc_type() { - let mut type1: TraitItemMethod = syn::parse2(quote! { - fn test(&self) -> Self::A; - }) - .unwrap(); - - let mut type_converter = TypeConverter::default(); - let ident = format_ident!("A"); - let dest_inner = Type::Verbatim(quote! {Example}); - let dest = DestType::Into(&dest_inner); - type_converter.assoc_type_conversions.insert(ident, dest); - - assert!(matches!( - parse_trait_signature(&mut type1.sig, &type_converter), - Ok(SignatureChanges { - return_type: TypeTransform::Into, - .. - }) - )); - } - - #[test] - fn err_unconvertible_assoc_type() { - let mut type1: TraitItemMethod = syn::parse2(quote! { - fn test(&self) -> Self::A; - }) - .unwrap(); - - assert!(matches!( - parse_trait_signature(&mut type1.sig, &Default::default()), - Err(( - _, - MethodError::Transform(TransformError::AssocTypeWithoutDestType) - )) - )); - } - - #[test] - fn err_non_dispatchable_assoc_function_no_args() { - let mut type1: TraitItemMethod = syn::parse2(quote! { - fn test(); - }) - .unwrap(); - - assert!(matches!( - parse_trait_signature(&mut type1.sig, &Default::default()), - Err((_, MethodError::NonDispatchableMethod)) - )); - } - - #[test] - fn err_non_dispatchable_assoc_function_with_args() { - let mut type1: TraitItemMethod = syn::parse2(quote! { - fn test(arg: Type); - }) - .unwrap(); - - assert!(matches!( - parse_trait_signature(&mut type1.sig, &Default::default()), - Err((_, MethodError::NonDispatchableMethod)) - )); - } - - #[test] - fn err_non_dispatchable_consume_self() { - let mut type1: TraitItemMethod = syn::parse2(quote! { - fn test(self); - }) - .unwrap(); - - assert!(matches!( - parse_trait_signature(&mut type1.sig, &Default::default()), - Err((_, MethodError::NonDispatchableMethod)) - )); - } - - #[test] - fn err_non_dispatchable_where_self_sized() { - let mut type1: TraitItemMethod = syn::parse2(quote! { - fn test(&self) where Self: Sized; - }) - .unwrap(); - - assert!(matches!( - parse_trait_signature(&mut type1.sig, &Default::default()), - Err((_, MethodError::NonDispatchableMethod)) - )); - } - - #[test] - fn err_assoc_type_in_unsupported_return() { - let mut type1: TraitItemMethod = syn::parse2(quote! { - fn test(&self) -> Foo<Self::A>; - }) - .unwrap(); - - assert!(matches!( - parse_trait_signature(&mut type1.sig, &Default::default()), - Err((_, MethodError::Transform(TransformError::UnsupportedType))) - )); - } - - #[test] - fn err_assoc_type_in_unsupported_return_in_opt() { - let mut type1: TraitItemMethod = syn::parse2(quote! { - fn test(&self) -> Option<Foo<Self::A>>; - }) - .unwrap(); - - assert!(matches!( - parse_trait_signature(&mut type1.sig, &Default::default()), - Err((_, MethodError::Transform(TransformError::UnsupportedType))) - )); - } - - #[test] - fn err_assoc_type_in_unsupported_return_in_ok() { - let mut type1: TraitItemMethod = syn::parse2(quote! { - fn test(&self) -> Result<Foo<Self::A>, Error>; - }) - .unwrap(); - - assert!(matches!( - parse_trait_signature(&mut type1.sig, &Default::default()), - Err((_, MethodError::Transform(TransformError::UnsupportedType))) - )); - } - - #[test] - fn err_assoc_type_in_unsupported_return_in_err() { - let mut type1: TraitItemMethod = syn::parse2(quote! { - fn test(&self) -> Result<Ok, Foo<Self::A>>; - }) - .unwrap(); - - assert!(matches!( - parse_trait_signature(&mut type1.sig, &Default::default()), - Err((_, MethodError::Transform(TransformError::UnsupportedType))) - )); - } - - #[test] - fn err_assoc_type_in_input() { - let mut type1: TraitItemMethod = syn::parse2(quote! { - fn test(&self, x: Self::A); - }) - .unwrap(); - - assert!(matches!( - parse_trait_signature(&mut type1.sig, &Default::default()), - Err((_, MethodError::AssocTypeInInputs)) - )); - } - - #[test] - fn err_assoc_type_in_input_opt() { - let mut type1: TraitItemMethod = syn::parse2(quote! { - fn test(&self, x: Option<Self::A>); - }) - .unwrap(); - - assert!(matches!( - parse_trait_signature(&mut type1.sig, &Default::default()), - Err((_, MethodError::AssocTypeInInputs)) - )); - } - - #[test] - fn err_impl_in_input() { - let mut type1: TraitItemMethod = syn::parse2(quote! { - fn test(&self, arg: Option<impl SomeTrait>); - }) - .unwrap(); - - assert!(matches!( - parse_trait_signature(&mut type1.sig, &Default::default()), - Err((_, MethodError::ImplTraitInInputs)) - )); - } - - #[test] - fn err_assoc_type_in_generic() { - let mut type1: TraitItemMethod = syn::parse2(quote! { - fn test<F: Fn(Foo<Self::A>)>(&self, fun: F); - }) - .unwrap(); - - assert!(matches!( - parse_trait_signature(&mut type1.sig, &Default::default()), - Err((_, MethodError::Transform(TransformError::UnsupportedType))) - )); - } -} |