From 21583e526c8045c60026b1fe8ec9510734d1764d Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Thu, 25 Nov 2021 08:33:06 +0100 Subject: rename parse_trait_sig module to trait_sig --- src/lib.rs | 12 +- src/parse_assoc_type.rs | 2 +- src/parse_trait_sig.rs | 361 ------------------------------------------------ src/trait_sig.rs | 361 ++++++++++++++++++++++++++++++++++++++++++++++++ src/transform.rs | 2 +- 5 files changed, 369 insertions(+), 369 deletions(-) delete mode 100644 src/parse_trait_sig.rs create mode 100644 src/trait_sig.rs diff --git a/src/lib.rs b/src/lib.rs index 00b9772..6f4a007 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,18 +44,18 @@ use syn_utils::TypeOrPath; use crate::parse_assoc_type::parse_assoc_type; use crate::parse_assoc_type::AssocTypeError; -use crate::parse_trait_sig::parse_trait_signature; -use crate::parse_trait_sig::MethodError; -use crate::parse_trait_sig::SignatureChanges; -use crate::parse_trait_sig::TypeTransform; use crate::syn_utils::iter_path; use crate::syn_utils::trait_bounds; +use crate::trait_sig::convert_trait_signature; +use crate::trait_sig::MethodError; +use crate::trait_sig::SignatureChanges; +use crate::trait_sig::TypeTransform; use crate::transform::TransformError; use crate::transform::TypeConverter; mod parse_assoc_type; -mod parse_trait_sig; mod syn_utils; +mod trait_sig; mod transform; macro_rules! abort { @@ -189,7 +189,7 @@ pub fn dynamize(_attr: TokenStream, input: TokenStream) -> TokenStream { for item in &original_trait.items { if let TraitItem::Method(method) = item { let mut signature = method.sig.clone(); - match parse_trait_signature(&mut signature, &type_converter) { + match convert_trait_signature(&mut signature, &type_converter) { Ok(parsed_method) => objectifiable_methods.push((signature, parsed_method)), Err((span, err)) => match err { MethodError::NonDispatchableMethod => continue, diff --git a/src/parse_assoc_type.rs b/src/parse_assoc_type.rs index a02e06c..adde237 100644 --- a/src/parse_assoc_type.rs +++ b/src/parse_assoc_type.rs @@ -4,8 +4,8 @@ use syn::spanned::Spanned; use syn::{GenericArgument, Ident, PathArguments, PathSegment, TraitItemType, Type}; use crate::match_assoc_type; -use crate::parse_trait_sig::TypeTransform; use crate::syn_utils::{iter_type, lifetime_bounds, trait_bounds}; +use crate::trait_sig::TypeTransform; #[derive(Debug)] pub enum AssocTypeError { 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), - Tuple(Vec), - IntoIterMapCollect(Vec), - Iterator(BoxType, Box), - Result(Box, Box), -} - -#[derive(Debug)] -pub enum MethodError { - NonDispatchableMethod, - AssocTypeInInputs, - ImplTraitInInputs, - - Transform(TransformError), - - UnconvertedAssocType, -} - -impl From 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>, -} - -pub fn parse_trait_signature( - signature: &mut Signature, - type_converter: &TypeConverter, -) -> Result { - 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; - }) - .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>; - }) - .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, 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>; - }) - .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); - }) - .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); - }) - .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)>(&self, fun: F); - }) - .unwrap(); - - assert!(matches!( - parse_trait_signature(&mut type1.sig, &Default::default()), - Err((_, MethodError::Transform(TransformError::UnsupportedType))) - )); - } -} diff --git a/src/trait_sig.rs b/src/trait_sig.rs new file mode 100644 index 0000000..88aac6a --- /dev/null +++ b/src/trait_sig.rs @@ -0,0 +1,361 @@ +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), + Tuple(Vec), + IntoIterMapCollect(Vec), + Iterator(BoxType, Box), + Result(Box, Box), +} + +#[derive(Debug)] +pub enum MethodError { + NonDispatchableMethod, + AssocTypeInInputs, + ImplTraitInInputs, + + Transform(TransformError), + + UnconvertedAssocType, +} + +impl From 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>, +} + +pub fn convert_trait_signature( + signature: &mut Signature, + type_converter: &TypeConverter, +) -> Result { + 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, + trait_sig::{convert_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!( + convert_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!( + convert_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!( + convert_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!( + convert_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!( + convert_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!( + convert_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!( + convert_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; + }) + .unwrap(); + + assert!(matches!( + convert_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>; + }) + .unwrap(); + + assert!(matches!( + convert_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, Error>; + }) + .unwrap(); + + assert!(matches!( + convert_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>; + }) + .unwrap(); + + assert!(matches!( + convert_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!( + convert_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); + }) + .unwrap(); + + assert!(matches!( + convert_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); + }) + .unwrap(); + + assert!(matches!( + convert_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)>(&self, fun: F); + }) + .unwrap(); + + assert!(matches!( + convert_trait_signature(&mut type1.sig, &Default::default()), + Err((_, MethodError::Transform(TransformError::UnsupportedType))) + )); + } +} diff --git a/src/transform.rs b/src/transform.rs index 594d383..831fb14 100644 --- a/src/transform.rs +++ b/src/transform.rs @@ -10,9 +10,9 @@ use syn::{ use crate::{ filter_map_assoc_paths, match_assoc_type, parse_assoc_type::{BoxType, DestType}, - parse_trait_sig::{MethodError, TypeTransform}, path_is_assoc_type, syn_utils::{iter_path, iter_type, type_arguments_mut}, + trait_sig::{MethodError, TypeTransform}, }; #[derive(Default)] -- cgit v1.2.3