From 1bbb5a1320fbe2ed9bb48742471be3c2594cca94 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Tue, 22 Aug 2023 12:27:15 +0300 Subject: [PATCH 01/58] [skip ci] Add type parameters to AsRef/AsMut attribute parsing --- impl/Cargo.toml | 2 +- impl/src/as/mod.rs | 218 +++++++++++++++++++++++++++++---------------- impl/src/lib.rs | 1 + 3 files changed, 141 insertions(+), 80 deletions(-) diff --git a/impl/Cargo.toml b/impl/Cargo.toml index 304a37ce..036b8499 100644 --- a/impl/Cargo.toml +++ b/impl/Cargo.toml @@ -48,7 +48,7 @@ default = [] add = [] add_assign = [] -as_ref = [] +as_ref = ["syn/extra-traits"] constructor = [] debug = ["syn/extra-traits", "dep:unicode-xid"] deref = [] diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index a750aa65..228f1659 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -8,11 +8,15 @@ use quote::{quote, ToTokens}; use syn::{ parse::{discouraged::Speculative as _, Parse, ParseStream}, parse_quote, + punctuated::Punctuated, spanned::Spanned, Token, }; -use crate::utils::{forward, skip, Either, Spanning}; +use crate::{ + parsing::Type, + utils::{forward, skip, Either, Spanning}, +}; /// Expands an [`AsRef`]/[`AsMut`] derive macro. pub fn expand( @@ -33,8 +37,8 @@ pub fn expand( )), }?; - let expansions = if StructAttribute::parse_attrs(&input.attrs, attr_ident)? - .is_some() + let expansions = if let Some(args) = + StructAttribute::parse_attrs(&input.attrs, attr_ident)? { if data.fields.len() != 1 { return Err(syn::Error::new( @@ -64,7 +68,7 @@ pub fn expand( generics: &input.generics, field, field_index: 0, - forward: true, + args: Some(args.into_inner()), }] } else { let attrs = data @@ -110,7 +114,7 @@ pub fn expand( generics: &input.generics, field, field_index: i, - forward: false, + args: None, }) }) .collect() @@ -120,14 +124,18 @@ pub fn expand( .enumerate() .zip(attrs) .filter_map(|((i, field), attr)| match attr.map(Spanning::into_inner) { - attr @ Some(FieldAttribute::Empty | FieldAttribute::Forward(_)) => { + attr @ Some(FieldAttribute::Empty | FieldAttribute::Args(_)) => { Some(Expansion { trait_info, ident: &input.ident, generics: &input.generics, field, field_index: i, - forward: matches!(attr, Some(FieldAttribute::Forward(_))), + args: if let Some(FieldAttribute::Args(args)) = attr { + Some(args) + } else { + None + }, }) } Some(FieldAttribute::Skip(_)) => unreachable!(), @@ -167,58 +175,58 @@ struct Expansion<'a> { field_index: usize, /// Indicator whether `forward` implementation should be expanded. - forward: bool, + args: Option, } impl<'a> ToTokens for Expansion<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { - let field_ty = &self.field.ty; - let field_ident = self.field.ident.as_ref().map_or_else( - || Either::Right(syn::Index::from(self.field_index)), - Either::Left, - ); - - let return_ty = if self.forward { - quote! { __AsT } - } else { - quote! { #field_ty } - }; - - let (trait_ident, method_ident, mut_) = &self.trait_info; - let trait_ty = quote! { ::core::convert::#trait_ident <#return_ty> }; - - let ty_ident = &self.ident; - let mut generics = self.generics.clone(); - if self.forward { - generics.params.push(parse_quote! { #return_ty }); - generics - .make_where_clause() - .predicates - .extend::<[syn::WherePredicate; 2]>([ - parse_quote! { #return_ty: ?::core::marker::Sized }, - parse_quote! { #field_ty: #trait_ty }, - ]); - } - let (impl_gens, _, where_clause) = generics.split_for_impl(); - let (_, ty_gens, _) = self.generics.split_for_impl(); - - let mut body = quote! { & #mut_ self.#field_ident }; - if self.forward { - body = quote! { - <#field_ty as #trait_ty>::#method_ident(#body) - }; - } - - quote! { - #[automatically_derived] - impl #impl_gens #trait_ty for #ty_ident #ty_gens #where_clause { - #[inline] - fn #method_ident(& #mut_ self) -> & #mut_ #return_ty { - #body - } - } - } - .to_tokens(tokens) + // let field_ty = &self.field.ty; + // let field_ident = self.field.ident.as_ref().map_or_else( + // || Either::Right(syn::Index::from(self.field_index)), + // Either::Left, + // ); + + // let return_ty = if self.forward { + // quote! { __AsT } + // } else { + // quote! { #field_ty } + // }; + + // let (trait_ident, method_ident, mut_) = &self.trait_info; + // let trait_ty = quote! { ::core::convert::#trait_ident <#return_ty> }; + + // let ty_ident = &self.ident; + // let mut generics = self.generics.clone(); + // if self.forward { + // generics.params.push(parse_quote! { #return_ty }); + // generics + // .make_where_clause() + // .predicates + // .extend::<[syn::WherePredicate; 2]>([ + // parse_quote! { #return_ty: ?::core::marker::Sized }, + // parse_quote! { #field_ty: #trait_ty }, + // ]); + // } + // let (impl_gens, _, where_clause) = generics.split_for_impl(); + // let (_, ty_gens, _) = self.generics.split_for_impl(); + + // let mut body = quote! { & #mut_ self.#field_ident }; + // if self.forward { + // body = quote! { + // <#field_ty as #trait_ty>::#method_ident(#body) + // }; + // } + + // quote! { + // #[automatically_derived] + // impl #impl_gens #trait_ty for #ty_ident #ty_gens #where_clause { + // #[inline] + // fn #method_ident(& #mut_ self) -> & #mut_ #return_ty { + // #body + // } + // } + // } + // .to_tokens(tokens) } } @@ -226,44 +234,42 @@ impl<'a> ToTokens for Expansion<'a> { /// /// ```rust,ignore /// #[as_ref(forward)] +/// #[as_ref()] /// ``` -type StructAttribute = forward::Attribute; +type StructAttribute = AsArgs; /// Representation of an [`AsRef`]/[`AsMut`] derive macro field attribute. /// /// ```rust,ignore /// #[as_ref] /// #[as_ref(forward)] +/// #[as_ref()] /// #[as_ref(skip)] #[as_ref(ignore)] /// ``` enum FieldAttribute { Empty, - Forward(forward::Attribute), + Args(AsArgs), Skip(skip::Attribute), } +enum AsArgs { + Forward(forward::Attribute), + Types(Punctuated), +} + impl Parse for FieldAttribute { fn parse(input: ParseStream<'_>) -> syn::Result { if input.is_empty() { return Ok(Self::Empty); } - let ahead = input.fork(); - if let Ok(attr) = ahead.parse::() { - input.advance_to(&ahead); - return Ok(Self::Forward(attr)); - } - let ahead = input.fork(); if let Ok(attr) = ahead.parse::() { input.advance_to(&ahead); return Ok(Self::Skip(attr)); } - Err(syn::Error::new( - input.span(), - "only `forward` or `skip`/`ignore` allowed here", - )) + input.parse::().map(Self::Args) } } @@ -275,21 +281,75 @@ impl FieldAttribute { attrs: &[syn::Attribute], attr_ident: &syn::Ident, ) -> syn::Result>> { - attrs.iter() + attrs + .iter() .filter(|attr| attr.path().is_ident(attr_ident)) - .try_fold(None, |mut attrs, attr| { - let parsed = Spanning::new(if matches!(attr.meta, syn::Meta::Path(_)) { - Self::Empty + .try_fold(None, |attrs: Option>, attr| { + let parsed = Spanning::new( + if matches!(attr.meta, syn::Meta::Path(_)) { + Self::Empty + } else { + attr.parse_args()? + }, + attr.span(), + ); + + + if let Some(prev) = attrs { + let span = prev.span.join(parsed.span).unwrap_or(prev.span); + match (prev.item, parsed.item) { + (Self::Args(AsArgs::Types(mut tys)), Self::Args(AsArgs::Types(more))) => { + tys.extend(more); + Ok(Some(Spanning::new(Self::Args(AsArgs::Types(tys)), span))) + } + _ => Err(syn::Error::new( + span, + format!("only single `#[{attr_ident}(...)]` attribute is allowed here") + )) + } } else { - attr.parse_args()? - }, attr.span()); - if attrs.replace(parsed).is_some() { - Err(syn::Error::new( - attr.span(), - format!("only single `#[{attr_ident}(...)]` attribute is allowed here"), - )) + Ok(Some(parsed)) + } + }) + } +} + +impl Parse for AsArgs { + fn parse(input: ParseStream) -> syn::Result { + let ahead = input.fork(); + if let Ok(attr) = ahead.parse::() { + return Ok(Self::Forward(attr)); + } + + input + .parse_terminated(Type::parse, Token![,]) + .map(Self::Types) + } +} + +impl StructAttribute { + fn parse_attrs( + attrs: &[syn::Attribute], + attr_ident: &syn::Ident, + ) -> syn::Result>> { + attrs.iter().filter(|attr| attr.path().is_ident(attr_ident)) + .try_fold(None, |attrs: Option>, attr| { + let parsed: Spanning = Spanning::new(attr.parse_args()?, attr.span()); + + if let Some(prev) = attrs { + let span = prev.span.join(parsed.span).unwrap_or(prev.span); + match (prev.item, parsed.item) { + (Self::Types(mut tys), Self::Types(more)) => { + tys.extend(more); + Ok(Some(Spanning::new(Self::Types(tys), span))) + }, + _ => Err(syn::Error::new( + span, + format!("only single `#[{attr_ident}(...)]` attribute is allowed here"), + )) + } } else { - Ok(attrs) + Ok(Some(parsed)) } }) } diff --git a/impl/src/lib.rs b/impl/src/lib.rs index 62c32700..bd6ecdc4 100644 --- a/impl/src/lib.rs +++ b/impl/src/lib.rs @@ -56,6 +56,7 @@ mod mul_like; #[cfg(feature = "not")] mod not_like; #[cfg(any( + feature = "as_ref", feature = "debug", feature = "display", feature = "from", From 059bf7b2ab39b2eec589fb92c3847ea61cf6db86 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Tue, 22 Aug 2023 14:02:53 +0300 Subject: [PATCH 02/58] Add handling for type parameters in AsRef/AsMut macro expansion --- impl/src/as/mod.rs | 123 +++++++++++++++++++++++++++------------------ 1 file changed, 75 insertions(+), 48 deletions(-) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index 228f1659..267cbb2a 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -3,6 +3,8 @@ pub(crate) mod r#mut; pub(crate) mod r#ref; +use std::borrow::Cow; + use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{ @@ -174,59 +176,77 @@ struct Expansion<'a> { /// Index of the [`syn::Field`]. field_index: usize, - /// Indicator whether `forward` implementation should be expanded. + /// Arguments on the attribute args: Option, } impl<'a> ToTokens for Expansion<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { - // let field_ty = &self.field.ty; - // let field_ident = self.field.ident.as_ref().map_or_else( - // || Either::Right(syn::Index::from(self.field_index)), - // Either::Left, - // ); - - // let return_ty = if self.forward { - // quote! { __AsT } - // } else { - // quote! { #field_ty } - // }; - - // let (trait_ident, method_ident, mut_) = &self.trait_info; - // let trait_ty = quote! { ::core::convert::#trait_ident <#return_ty> }; - - // let ty_ident = &self.ident; - // let mut generics = self.generics.clone(); - // if self.forward { - // generics.params.push(parse_quote! { #return_ty }); - // generics - // .make_where_clause() - // .predicates - // .extend::<[syn::WherePredicate; 2]>([ - // parse_quote! { #return_ty: ?::core::marker::Sized }, - // parse_quote! { #field_ty: #trait_ty }, - // ]); - // } - // let (impl_gens, _, where_clause) = generics.split_for_impl(); - // let (_, ty_gens, _) = self.generics.split_for_impl(); - - // let mut body = quote! { & #mut_ self.#field_ident }; - // if self.forward { - // body = quote! { - // <#field_ty as #trait_ty>::#method_ident(#body) - // }; - // } - - // quote! { - // #[automatically_derived] - // impl #impl_gens #trait_ty for #ty_ident #ty_gens #where_clause { - // #[inline] - // fn #method_ident(& #mut_ self) -> & #mut_ #return_ty { - // #body - // } - // } - // } - // .to_tokens(tokens) + let field_ty = &self.field.ty; + let field_ident = self.field.ident.as_ref().map_or_else( + || Either::Right(syn::Index::from(self.field_index)), + Either::Left, + ); + + let (trait_ident, method_ident, mut_) = &self.trait_info; + + let ty_ident = &self.ident; + + let (blanket, forward_impl, return_tys) = match &self.args { + Some(AsArgs::Forward(_)) => { + (true, true, Either::Left(std::iter::once(quote! { __AsT }))) + } + Some(AsArgs::Types(tys)) => ( + false, + true, + Either::Right(tys.iter().map(ToTokens::into_token_stream)), + ), + None => ( + false, + false, + Either::Left(std::iter::once(quote! { #field_ty })), + ), + }; + + return_tys + .map(|return_ty| { + let trait_ty = quote! { ::core::convert::#trait_ident <#return_ty> }; + + let generics = if blanket { + let mut generics = self.generics.clone(); + generics.params.push(parse_quote! { #return_ty }); + generics + .make_where_clause() + .predicates + .extend::<[syn::WherePredicate; 2]>([ + parse_quote! { #return_ty: ?::core::marker::Sized }, + parse_quote! { #field_ty: #trait_ty }, + ]); + Cow::Owned(generics) + } else { + Cow::Borrowed(self.generics) + }; + + let (impl_gens, _, where_clause) = generics.split_for_impl(); + let (_, ty_gens, _) = self.generics.split_for_impl(); + + let mut body = quote! { & #mut_ self.#field_ident }; + if forward_impl { + body = quote! { <#field_ty as #trait_ty>::#method_ident(#body) }; + } + + quote! { + #[automatically_derived] + impl #impl_gens #trait_ty for #ty_ident #ty_gens #where_clause { + #[inline] + fn #method_ident(& #mut_ self) -> & #mut_ #return_ty { + #body + } + } + } + }) + .collect::() + .to_tokens(tokens); } } @@ -252,8 +272,11 @@ enum FieldAttribute { Skip(skip::Attribute), } +/// Arguments specifying which conversions should be generated enum AsArgs { + /// Blanket impl, fully forwarding to the field type Forward(forward::Attribute), + /// Forward implementation, but only impl for specified types Types(Punctuated), } @@ -318,6 +341,7 @@ impl Parse for AsArgs { fn parse(input: ParseStream) -> syn::Result { let ahead = input.fork(); if let Ok(attr) = ahead.parse::() { + input.advance_to(&ahead); return Ok(Self::Forward(attr)); } @@ -328,6 +352,9 @@ impl Parse for AsArgs { } impl StructAttribute { + /// Parses a [`StructAttribute`] from the provided [`syn::Attribute`]s, preserving its [`Span`]. + /// + /// [`Span`]: proc_macro2::Span fn parse_attrs( attrs: &[syn::Attribute], attr_ident: &syn::Ident, From a541e6d267c3ecbd7675f65883bef09efdf26d11 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Tue, 22 Aug 2023 14:36:10 +0300 Subject: [PATCH 03/58] Fix generated bounds --- impl/src/as/mod.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index 267cbb2a..6ae4ec48 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -212,16 +212,20 @@ impl<'a> ToTokens for Expansion<'a> { .map(|return_ty| { let trait_ty = quote! { ::core::convert::#trait_ident <#return_ty> }; - let generics = if blanket { + let generics = if forward_impl { let mut generics = self.generics.clone(); - generics.params.push(parse_quote! { #return_ty }); + if blanket { + generics.params.push(parse_quote! { #return_ty }); + generics + .make_where_clause() + .predicates + .push(parse_quote! { #return_ty: ?::core::marker::Sized }); + } generics .make_where_clause() .predicates - .extend::<[syn::WherePredicate; 2]>([ - parse_quote! { #return_ty: ?::core::marker::Sized }, - parse_quote! { #field_ty: #trait_ty }, - ]); + .push(parse_quote! { #field_ty: #trait_ty }); + Cow::Owned(generics) } else { Cow::Borrowed(self.generics) From 62978f3cd18a3a710f10785aaeb1fb035c4ea5cf Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Tue, 22 Aug 2023 14:40:03 +0300 Subject: [PATCH 04/58] Add tests for AsRef/AsMut type arguments --- tests/as_mut.rs | 273 ++++++++++++++++++++++++++++++++++++++++++++++++ tests/as_ref.rs | 273 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 546 insertions(+) diff --git a/tests/as_mut.rs b/tests/as_mut.rs index 846ab117..982a0f4b 100644 --- a/tests/as_mut.rs +++ b/tests/as_mut.rs @@ -14,6 +14,9 @@ use core::ptr; use derive_more::AsMut; +#[derive(AsMut)] +struct Foo(i32, f64, bool); + mod single_field { use super::*; @@ -63,6 +66,51 @@ mod single_field { assert!(ptr::eq(rf, item.0.as_mut())); } + #[derive(AsMut)] + #[as_mut(i32, f64)] + struct Types(Foo); + + // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsMut for Types { + fn as_mut(&mut self) -> &mut bool { + self.0.as_mut() + } + } + + #[test] + fn types() { + let mut item = Types(Foo(1, 2.0, false)); + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.0.as_mut())); + + let rf: &mut f64 = item.as_mut(); + assert!(ptr::eq(rf, item.0.as_mut())); + } + + #[derive(AsMut)] + struct FieldTypes(#[as_mut(i32, f64)] Foo); + + // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsMut for FieldTypes { + fn as_mut(&mut self) -> &mut bool { + self.0.as_mut() + } + } + + #[test] + fn field_types() { + let mut item = FieldTypes(Foo(1, 2.0, false)); + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.0.as_mut())); + + let rf: &mut f64 = item.as_mut(); + assert!(ptr::eq(rf, item.0.as_mut())); + } + mod generic { use super::*; @@ -108,6 +156,51 @@ mod single_field { let rf: &mut str = item.as_mut(); assert!(ptr::eq(rf, item.0.as_mut())); } + + #[derive(AsMut)] + #[as_mut(i32, f64)] + struct Types(T); + + // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl> AsMut for Types { + fn as_mut(&mut self) -> &mut bool { + self.0.as_mut() + } + } + + #[test] + fn types() { + let mut item = Types(Foo(1, 2.0, false)); + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.0.as_mut())); + + let rf: &mut f64 = item.as_mut(); + assert!(ptr::eq(rf, item.0.as_mut())); + } + + #[derive(AsMut)] + struct FieldTypes(#[as_mut(i32, f64)] T); + + // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl> AsMut for FieldTypes { + fn as_mut(&mut self) -> &mut bool { + self.0.as_mut() + } + } + + #[test] + fn field_types() { + let mut item = FieldTypes(Foo(1, 2.0, false)); + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.0.as_mut())); + + let rf: &mut f64 = item.as_mut(); + assert!(ptr::eq(rf, item.0.as_mut())); + } } } @@ -175,6 +268,60 @@ mod single_field { assert!(ptr::eq(rf, item.first.as_mut())); } + #[derive(AsMut)] + #[as_mut(i32, f64)] + struct Types { + first: Foo, + } + + // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsMut for Types { + fn as_mut(&mut self) -> &mut bool { + self.first.as_mut() + } + } + + #[test] + fn types() { + let mut item = Types { + first: Foo(1, 2.0, false), + }; + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.first.as_mut())); + + let rf: &mut f64 = item.as_mut(); + assert!(ptr::eq(rf, item.first.as_mut())); + } + + #[derive(AsMut)] + struct FieldTypes { + #[as_mut(i32, f64)] + first: Foo, + } + + // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsMut for FieldTypes { + fn as_mut(&mut self) -> &mut bool { + self.first.as_mut() + } + } + + #[test] + fn field_types() { + let mut item = FieldTypes { + first: Foo(1, 2.0, false), + }; + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.first.as_mut())); + + let rf: &mut f64 = item.as_mut(); + assert!(ptr::eq(rf, item.first.as_mut())); + } + mod generic { use super::*; @@ -238,6 +385,60 @@ mod single_field { let rf: &mut str = item.as_mut(); assert!(ptr::eq(rf, item.first.as_mut())); } + + #[derive(AsMut)] + #[as_mut(i32, f64)] + struct Types { + first: T, + } + + // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl> AsMut for Types { + fn as_mut(&mut self) -> &mut bool { + self.first.as_mut() + } + } + + #[test] + fn types() { + let mut item = Types { + first: Foo(1, 2.0, false), + }; + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.first.as_mut())); + + let rf: &mut f64 = item.as_mut(); + assert!(ptr::eq(rf, item.first.as_mut())); + } + + #[derive(AsMut)] + struct FieldTypes { + #[as_mut(i32, f64)] + first: T, + } + + // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl> AsMut for FieldTypes { + fn as_mut(&mut self) -> &mut bool { + self.first.as_mut() + } + } + + #[test] + fn field_types() { + let mut item = FieldTypes { + first: Foo(1, 2.0, false), + }; + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.first.as_mut())); + + let rf: &mut f64 = item.as_mut(); + assert!(ptr::eq(rf, item.first.as_mut())); + } } } } @@ -308,6 +509,20 @@ mod multi_field { assert!(ptr::eq(rf, item.0.as_mut())); } + #[derive(AsMut)] + struct Types(#[as_mut(str)] String, #[as_mut([u8])] Vec); + + #[test] + fn types() { + let mut item = Types("test".to_owned(), vec![0]); + + let rf: &mut str = item.as_mut(); + assert!(ptr::eq(rf, item.0.as_mut())); + + let rf: &mut [u8] = item.as_mut(); + assert!(ptr::eq(rf, item.1.as_mut())); + } + mod generic { use super::*; @@ -354,6 +569,20 @@ mod multi_field { let rf: &mut str = item.as_mut(); assert!(ptr::eq(rf, item.0.as_mut())); } + + #[derive(AsMut)] + struct Types(#[as_mut(str)] T, #[as_mut([u8])] U); + + #[test] + fn types() { + let mut item = Types("test".to_owned(), vec![0]); + + let rf: &mut str = item.as_mut(); + assert!(ptr::eq(rf, item.0.as_mut())); + + let rf: &mut [u8] = item.as_mut(); + assert!(ptr::eq(rf, item.1.as_mut())); + } } } @@ -452,6 +681,28 @@ mod multi_field { assert!(ptr::eq(rf, item.first.as_mut())); } + #[derive(AsMut)] + struct Types { + #[as_mut(str)] + first: String, + #[as_mut([u8])] + second: Vec, + } + + #[test] + fn types() { + let mut item = Types { + first: "test".to_owned(), + second: vec![0], + }; + + let rf: &mut str = item.as_mut(); + assert!(ptr::eq(rf, item.first.as_mut())); + + let rf: &mut [u8] = item.as_mut(); + assert!(ptr::eq(rf, item.second.as_mut())); + } + mod generic { use super::*; @@ -530,6 +781,28 @@ mod multi_field { let rf: &mut str = item.as_mut(); assert!(ptr::eq(rf, item.first.as_mut())); } + + #[derive(AsMut)] + struct Types { + #[as_mut(str)] + first: T, + #[as_mut([u8])] + second: U, + } + + #[test] + fn types() { + let mut item = Types { + first: "test".to_owned(), + second: vec![0], + }; + + let rf: &mut str = item.as_mut(); + assert!(ptr::eq(rf, item.first.as_mut())); + + let rf: &mut [u8] = item.as_mut(); + assert!(ptr::eq(rf, item.second.as_mut())); + } } } } diff --git a/tests/as_ref.rs b/tests/as_ref.rs index 35512452..22d8c707 100644 --- a/tests/as_ref.rs +++ b/tests/as_ref.rs @@ -14,6 +14,9 @@ use core::ptr; use derive_more::AsRef; +#[derive(AsRef)] +struct Foo(i32, f64, bool); + mod single_field { use super::*; @@ -63,6 +66,51 @@ mod single_field { assert!(ptr::eq(rf, item.0.as_ref())); } + #[derive(AsRef)] + #[as_ref(i32, f64)] + struct Types(Foo); + + // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsRef for Types { + fn as_ref(&self) -> &bool { + self.0.as_ref() + } + } + + #[test] + fn types() { + let item = Types(Foo(1, 2.0, false)); + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.0.as_ref())); + + let rf: &f64 = item.as_ref(); + assert!(ptr::eq(rf, item.0.as_ref())); + } + + #[derive(AsRef)] + struct FieldTypes(#[as_ref(i32, f64)] Foo); + + // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsRef for FieldTypes { + fn as_ref(&self) -> &bool { + self.0.as_ref() + } + } + + #[test] + fn field_types() { + let item = FieldTypes(Foo(1, 2.0, false)); + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.0.as_ref())); + + let rf: &f64 = item.as_ref(); + assert!(ptr::eq(rf, item.0.as_ref())); + } + mod generic { use super::*; @@ -108,6 +156,51 @@ mod single_field { let rf: &str = item.as_ref(); assert!(ptr::eq(rf, item.0.as_ref())); } + + #[derive(AsRef)] + #[as_ref(i32, f64)] + struct Types(T); + + // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl> AsRef for Types { + fn as_ref(&self) -> &bool { + self.0.as_ref() + } + } + + #[test] + fn types() { + let item = Types(Foo(1, 2.0, false)); + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.0.as_ref())); + + let rf: &f64 = item.as_ref(); + assert!(ptr::eq(rf, item.0.as_ref())); + } + + #[derive(AsRef)] + struct FieldTypes(#[as_ref(i32, f64)] T); + + // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl> AsRef for FieldTypes { + fn as_ref(&self) -> &bool { + self.0.as_ref() + } + } + + #[test] + fn field_types() { + let item = FieldTypes(Foo(1, 2.0, false)); + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.0.as_ref())); + + let rf: &f64 = item.as_ref(); + assert!(ptr::eq(rf, item.0.as_ref())); + } } } @@ -175,6 +268,60 @@ mod single_field { assert!(ptr::eq(rf, item.first.as_ref())); } + #[derive(AsRef)] + #[as_ref(i32, f64)] + struct Types { + first: Foo, + } + + // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsRef for Types { + fn as_ref(&self) -> &bool { + self.first.as_ref() + } + } + + #[test] + fn types() { + let item = Types { + first: Foo(1, 2.0, false), + }; + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.first.as_ref())); + + let rf: &f64 = item.as_ref(); + assert!(ptr::eq(rf, item.first.as_ref())); + } + + #[derive(AsRef)] + struct FieldTypes { + #[as_ref(i32, f64)] + first: Foo, + } + + // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsRef for FieldTypes { + fn as_ref(&self) -> &bool { + self.first.as_ref() + } + } + + #[test] + fn field_types() { + let item = FieldTypes { + first: Foo(1, 2.0, false), + }; + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.first.as_ref())); + + let rf: &f64 = item.as_ref(); + assert!(ptr::eq(rf, item.first.as_ref())); + } + mod generic { use super::*; @@ -238,6 +385,60 @@ mod single_field { let rf: &str = item.as_ref(); assert!(ptr::eq(rf, item.first.as_ref())); } + + #[derive(AsRef)] + #[as_ref(i32, f64)] + struct Types { + first: T, + } + + // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl> AsRef for Types { + fn as_ref(&self) -> &bool { + self.first.as_ref() + } + } + + #[test] + fn types() { + let item = Types { + first: Foo(1, 2.0, false), + }; + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.first.as_ref())); + + let rf: &f64 = item.as_ref(); + assert!(ptr::eq(rf, item.first.as_ref())); + } + + #[derive(AsRef)] + struct FieldTypes { + #[as_ref(i32, f64)] + first: T, + } + + // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl> AsRef for FieldTypes { + fn as_ref(&self) -> &bool { + self.first.as_ref() + } + } + + #[test] + fn field_types() { + let item = FieldTypes { + first: Foo(1, 2.0, false), + }; + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.first.as_ref())); + + let rf: &f64 = item.as_ref(); + assert!(ptr::eq(rf, item.first.as_ref())); + } } } } @@ -308,6 +509,20 @@ mod multi_field { assert!(ptr::eq(rf, item.0.as_ref())); } + #[derive(AsRef)] + struct Types(#[as_ref(str)] String, #[as_ref([u8])] Vec); + + #[test] + fn types() { + let item = Types("test".to_owned(), vec![0]); + + let rf: &str = item.as_ref(); + assert!(ptr::eq(rf, item.0.as_ref())); + + let rf: &[u8] = item.as_ref(); + assert!(ptr::eq(rf, item.1.as_ref())); + } + mod generic { use super::*; @@ -354,6 +569,20 @@ mod multi_field { let rf: &str = item.as_ref(); assert!(ptr::eq(rf, item.0.as_ref())); } + + #[derive(AsRef)] + struct Types(#[as_ref(str)] T, #[as_ref([u8])] U); + + #[test] + fn types() { + let item = Types("test".to_owned(), vec![0u8]); + + let rf: &str = item.as_ref(); + assert!(ptr::eq(rf, item.0.as_ref())); + + let rf: &[u8] = item.as_ref(); + assert!(ptr::eq(rf, item.1.as_ref())); + } } } @@ -452,6 +681,28 @@ mod multi_field { assert!(ptr::eq(rf, item.first.as_ref())); } + #[derive(AsRef)] + struct Types { + #[as_ref(str)] + first: String, + #[as_ref([u8])] + second: Vec, + } + + #[test] + fn types() { + let item = Types { + first: "test".to_owned(), + second: vec![0u8], + }; + + let rf: &str = item.as_ref(); + assert!(ptr::eq(rf, item.first.as_ref())); + + let rf: &[u8] = item.as_ref(); + assert!(ptr::eq(rf, item.second.as_ref())); + } + mod generic { use super::*; @@ -530,6 +781,28 @@ mod multi_field { let rf: &str = item.as_ref(); assert!(ptr::eq(rf, item.first.as_ref())); } + + #[derive(AsRef)] + struct Types { + #[as_ref(str)] + first: T, + #[as_ref([u8])] + second: U, + } + + #[test] + fn types() { + let item = Types { + first: "test".to_owned(), + second: vec![0u8], + }; + + let rf: &str = item.as_ref(); + assert!(ptr::eq(rf, item.first.as_ref())); + + let rf: &[u8] = item.as_ref(); + assert!(ptr::eq(rf, item.second.as_ref())); + } } } } From d3edb4228e9806fe6b35856277711fc4eaad0a8f Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Tue, 22 Aug 2023 15:03:44 +0300 Subject: [PATCH 05/58] Update docs with type parameter details --- impl/doc/as_mut.md | 54 +++++++++++++++++++++++++++++++++++++++++++- impl/doc/as_ref.md | 56 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 2 deletions(-) diff --git a/impl/doc/as_mut.md b/impl/doc/as_mut.md index a7f80571..9f3d7241 100644 --- a/impl/doc/as_mut.md +++ b/impl/doc/as_mut.md @@ -31,7 +31,7 @@ impl AsMut for MyWrapper { } ``` -It's also possible to use the `#[as_mut(forward)]` attribute to forward +The `#[as_mut(forward)]` attribute can be used to forward to the `as_mut` implementation of the field. So here `SingleFieldForward` implements all `AsMut` for all types that `Vec` implements `AsMut` for. @@ -61,7 +61,37 @@ where } ``` +It's also possible to specify concrete types to derive forwarded +impls for with `#[as_mut()]`. +```rust +# use derive_more::AsMut; +# +#[derive(AsMut)] +#[as_mut(str, [u8])] +struct Types(String); + +let mut item = Types("test".to_owned()); +let _: &mut str = item.as_mut(); +let _: &mut [u8] = item.as_mut(); +``` + +Generates: + +```rust +# struct Types(String); +impl AsMut for Types { + fn as_mut(&mut self) -> &mut str { + >::as_mut(&mut self.0) + } +} + +impl AsMut<[u8]> for Types { + fn as_mut(&mut self) -> &mut [u8] { + >::as_mut(&mut self.0) + } +} +``` ## Structs with Multiple Fields @@ -139,6 +169,28 @@ struct ForwardWithOther { } ``` +Multiple forwarded impls with concrete types, however, can be used. + +```rust +# use derive_more::AsMut; +# +#[derive(AsMut)] +struct Types { + #[as_mut(str)] + str: String, + #[as_mut([u8])] + vec: Vec, +} + +let mut item = Types { + str: "test".to_owned(), + vec: vec![0u8], +}; + +let _: &mut str = item.as_mut(); +let _: &mut [u8] = item.as_mut(); +``` + ## Enums Deriving `AsMut` for enums is not supported. diff --git a/impl/doc/as_ref.md b/impl/doc/as_ref.md index 549e9155..90344e73 100644 --- a/impl/doc/as_ref.md +++ b/impl/doc/as_ref.md @@ -31,7 +31,7 @@ impl AsRef for MyWrapper { } ``` -It's also possible to use the `#[as_ref(forward)]` attribute to forward +The `#[as_ref(forward)]` attribute can be used to forward to the `as_ref` implementation of the field. So here `SingleFieldForward` implements all `AsRef` for all types that `Vec` implements `AsRef` for. @@ -61,6 +61,37 @@ where } ``` +It's also possible to specify concrete types to derive forwarded +impls for with `#[as_ref()]`. + +```rust +# use derive_more::AsRef; +# +#[derive(AsRef)] +#[as_ref(str, [u8])] +struct Types(String); + +let item = Types("test".to_owned()); +let _: &str = item.as_ref(); +let _: &[u8] = item.as_ref(); +``` + +Generates: + +```rust +# struct Types(String); +impl AsRef for Types { + fn as_ref(&self) -> &str { + >::as_ref(&self.0) + } +} + +impl AsRef<[u8]> for Types { + fn as_ref(&self) -> &[u8] { + >::as_ref(&self.0) + } +} +``` @@ -140,6 +171,29 @@ struct ForwardWithOther { } ``` +Multiple forwarded impls with concrete types, however, can be used. + +```rust +# use derive_more::AsRef; +# +#[derive(AsRef)] +struct Types { + #[as_ref(str)] + str: String, + #[as_ref([u8])] + vec: Vec, +} + +let item = Types { + str: "test".to_owned(), + vec: vec![0u8], +}; + +let _: &str = item.as_ref(); +let _: &[u8] = item.as_ref(); +``` + + ## Enums Deriving `AsRef` for enums is not supported. From 084495f83e774307e49cb12f71daa3b183ee67e4 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Tue, 22 Aug 2023 18:16:23 +0300 Subject: [PATCH 06/58] Improve doc comments --- impl/src/as/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index 6ae4ec48..36a10308 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -158,7 +158,7 @@ pub fn expand( /// - Optional `mut` token indicating [`AsMut`] expansion. type ExpansionCtx<'a> = (&'a syn::Ident, &'a syn::Ident, Option<&'a Token![mut]>); -/// Expansion of a macro for generating [`AsRef`]/[`AsMut`] implementation for a single field of a +/// Expansion of a macro for generating [`AsRef`]/[`AsMut`] implementations for a single field of a /// struct. struct Expansion<'a> { /// [`ExpansionCtx] of the derived trait. @@ -176,7 +176,7 @@ struct Expansion<'a> { /// Index of the [`syn::Field`]. field_index: usize, - /// Arguments on the attribute + /// Arguments specifying which conversions should be generated args: Option, } From 844cb92a3ae79bb32f41f53f81870dd975df1870 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Tue, 22 Aug 2023 18:35:08 +0300 Subject: [PATCH 07/58] Restore previous error span on multiple-attribute errors --- impl/src/as/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index 36a10308..250b3b01 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -330,7 +330,7 @@ impl FieldAttribute { Ok(Some(Spanning::new(Self::Args(AsArgs::Types(tys)), span))) } _ => Err(syn::Error::new( - span, + parsed.span, format!("only single `#[{attr_ident}(...)]` attribute is allowed here") )) } @@ -375,7 +375,7 @@ impl StructAttribute { Ok(Some(Spanning::new(Self::Types(tys), span))) }, _ => Err(syn::Error::new( - span, + parsed.span, format!("only single `#[{attr_ident}(...)]` attribute is allowed here"), )) } From 398fbe170e6911ad321f7ce5fe443fe60b8a8213 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Tue, 22 Aug 2023 18:35:17 +0300 Subject: [PATCH 08/58] Update compile-fail tests --- .../as_mut/multiple_struct_attrs.stderr | 2 +- .../as_mut/unknown_field_attr_arg.stderr | 15 +++++++++++++-- .../as_mut/unknown_struct_attr_arg.stderr | 15 +++++++++++++-- .../as_ref/multiple_struct_attrs.stderr | 2 +- .../as_ref/unknown_field_attr_arg.stderr | 15 +++++++++++++-- .../as_ref/unknown_struct_attr_arg.stderr | 15 +++++++++++++-- 6 files changed, 54 insertions(+), 10 deletions(-) diff --git a/tests/compile_fail/as_mut/multiple_struct_attrs.stderr b/tests/compile_fail/as_mut/multiple_struct_attrs.stderr index cec25f89..39d0d576 100644 --- a/tests/compile_fail/as_mut/multiple_struct_attrs.stderr +++ b/tests/compile_fail/as_mut/multiple_struct_attrs.stderr @@ -1,4 +1,4 @@ -error: only single `#[as_mut(forward)]` attribute is allowed here +error: only single `#[as_mut(...)]` attribute is allowed here --> tests/compile_fail/as_mut/multiple_struct_attrs.rs:3:1 | 3 | #[as_mut(forward)] diff --git a/tests/compile_fail/as_mut/unknown_field_attr_arg.stderr b/tests/compile_fail/as_mut/unknown_field_attr_arg.stderr index b191e1a6..59dcc8a7 100644 --- a/tests/compile_fail/as_mut/unknown_field_attr_arg.stderr +++ b/tests/compile_fail/as_mut/unknown_field_attr_arg.stderr @@ -1,5 +1,16 @@ -error: only `forward` or `skip`/`ignore` allowed here +error[E0412]: cannot find type `baz` in this scope --> tests/compile_fail/as_mut/unknown_field_attr_arg.rs:3:14 | 3 | #[as_mut(baz)] - | ^^^ + | ^^^ not found in this scope + | +help: you might be missing a type parameter + | +1 | #[derive(derive_more::AsMut)] + | +++++ + +error[E0412]: cannot find type `baz` in this scope + --> tests/compile_fail/as_mut/unknown_field_attr_arg.rs:3:14 + | +3 | #[as_mut(baz)] + | ^^^ not found in this scope diff --git a/tests/compile_fail/as_mut/unknown_struct_attr_arg.stderr b/tests/compile_fail/as_mut/unknown_struct_attr_arg.stderr index 3699acfd..5ca5d77f 100644 --- a/tests/compile_fail/as_mut/unknown_struct_attr_arg.stderr +++ b/tests/compile_fail/as_mut/unknown_struct_attr_arg.stderr @@ -1,5 +1,16 @@ -error: only `forward` allowed here +error[E0412]: cannot find type `baz` in this scope --> tests/compile_fail/as_mut/unknown_struct_attr_arg.rs:2:10 | 2 | #[as_mut(baz)] - | ^^^ + | ^^^ not found in this scope + | +help: you might be missing a type parameter + | +1 | #[derive(derive_more::AsMut)] + | +++++ + +error[E0412]: cannot find type `baz` in this scope + --> tests/compile_fail/as_mut/unknown_struct_attr_arg.rs:2:10 + | +2 | #[as_mut(baz)] + | ^^^ not found in this scope diff --git a/tests/compile_fail/as_ref/multiple_struct_attrs.stderr b/tests/compile_fail/as_ref/multiple_struct_attrs.stderr index 9baec8be..71416904 100644 --- a/tests/compile_fail/as_ref/multiple_struct_attrs.stderr +++ b/tests/compile_fail/as_ref/multiple_struct_attrs.stderr @@ -1,4 +1,4 @@ -error: only single `#[as_ref(forward)]` attribute is allowed here +error: only single `#[as_ref(...)]` attribute is allowed here --> tests/compile_fail/as_ref/multiple_struct_attrs.rs:3:1 | 3 | #[as_ref(forward)] diff --git a/tests/compile_fail/as_ref/unknown_field_attr_arg.stderr b/tests/compile_fail/as_ref/unknown_field_attr_arg.stderr index 90f6751b..01673bd1 100644 --- a/tests/compile_fail/as_ref/unknown_field_attr_arg.stderr +++ b/tests/compile_fail/as_ref/unknown_field_attr_arg.stderr @@ -1,5 +1,16 @@ -error: only `forward` or `skip`/`ignore` allowed here +error[E0412]: cannot find type `baz` in this scope --> tests/compile_fail/as_ref/unknown_field_attr_arg.rs:3:14 | 3 | #[as_ref(baz)] - | ^^^ + | ^^^ not found in this scope + | +help: you might be missing a type parameter + | +1 | #[derive(derive_more::AsRef)] + | +++++ + +error[E0412]: cannot find type `baz` in this scope + --> tests/compile_fail/as_ref/unknown_field_attr_arg.rs:3:14 + | +3 | #[as_ref(baz)] + | ^^^ not found in this scope diff --git a/tests/compile_fail/as_ref/unknown_struct_attr_arg.stderr b/tests/compile_fail/as_ref/unknown_struct_attr_arg.stderr index 760b9827..3bbde6ea 100644 --- a/tests/compile_fail/as_ref/unknown_struct_attr_arg.stderr +++ b/tests/compile_fail/as_ref/unknown_struct_attr_arg.stderr @@ -1,5 +1,16 @@ -error: only `forward` allowed here +error[E0412]: cannot find type `baz` in this scope --> tests/compile_fail/as_ref/unknown_struct_attr_arg.rs:2:10 | 2 | #[as_ref(baz)] - | ^^^ + | ^^^ not found in this scope + | +help: you might be missing a type parameter + | +1 | #[derive(derive_more::AsRef)] + | +++++ + +error[E0412]: cannot find type `baz` in this scope + --> tests/compile_fail/as_ref/unknown_struct_attr_arg.rs:2:10 + | +2 | #[as_ref(baz)] + | ^^^ not found in this scope From 04686cc9833f53d71cb33880ba7cea46c475f5f8 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Tue, 22 Aug 2023 18:37:11 +0300 Subject: [PATCH 09/58] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8bee80e..08676c21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Add support for container format in `Debug` derive with the same syntax as `Display` derives. ([#279](https://github.com/JelteF/derive_more/pull/279)) - `derive_more::derive` module exporting only macros, without traits. ([#290](https://github.com/JelteF/derive_more/pull/290)) +- Add support for selective forwarding in the `AsRef`/`AsMut` derive + ([#298](https://github.com/JelteF/derive_more/pull/298)) ### Changed From acbcabbc4d698414add22b3f45bbd8841419465a Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Tue, 22 Aug 2023 18:46:50 +0300 Subject: [PATCH 10/58] Refactor out FieldAttribute::into_args function --- impl/src/as/mod.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index 250b3b01..3db8534a 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -126,18 +126,14 @@ pub fn expand( .enumerate() .zip(attrs) .filter_map(|((i, field), attr)| match attr.map(Spanning::into_inner) { - attr @ Some(FieldAttribute::Empty | FieldAttribute::Args(_)) => { + Some(attr @ (FieldAttribute::Empty | FieldAttribute::Args(_))) => { Some(Expansion { trait_info, ident: &input.ident, generics: &input.generics, field, field_index: i, - args: if let Some(FieldAttribute::Args(args)) = attr { - Some(args) - } else { - None - }, + args: attr.into_args(), }) } Some(FieldAttribute::Skip(_)) => unreachable!(), @@ -339,6 +335,13 @@ impl FieldAttribute { } }) } + + fn into_args(self) -> Option { + match self { + Self::Args(args) => Some(args), + Self::Empty | Self::Skip(_) => None, + } + } } impl Parse for AsArgs { From a9cbe196510ac66aa8c2f94845d7854f16ea5c82 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Tue, 22 Aug 2023 18:49:59 +0300 Subject: [PATCH 11/58] Improve naming and docs --- impl/src/as/mod.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index 3db8534a..d082c8d9 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -39,7 +39,7 @@ pub fn expand( )), }?; - let expansions = if let Some(args) = + let expansions = if let Some(attr) = StructAttribute::parse_attrs(&input.attrs, attr_ident)? { if data.fields.len() != 1 { @@ -70,7 +70,7 @@ pub fn expand( generics: &input.generics, field, field_index: 0, - args: Some(args.into_inner()), + args: Some(attr.into_inner()), }] } else { let attrs = data @@ -273,6 +273,11 @@ enum FieldAttribute { } /// Arguments specifying which conversions should be generated +/// +/// ```rust,ignore +/// #[as_ref(forward)] +/// #[as_ref()]x +/// ``` enum AsArgs { /// Blanket impl, fully forwarding to the field type Forward(forward::Attribute), @@ -336,6 +341,7 @@ impl FieldAttribute { }) } + /// Conversion into arguments on the attribute, if any fn into_args(self) -> Option { match self { Self::Args(args) => Some(args), From b255a03bf565e5de4b16c4d77979e72706160c00 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Tue, 22 Aug 2023 18:59:28 +0300 Subject: [PATCH 12/58] Remove unused parsing helper --- impl/src/utils.rs | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/impl/src/utils.rs b/impl/src/utils.rs index 8b5fe51c..c8ab138b 100644 --- a/impl/src/utils.rs +++ b/impl/src/utils.rs @@ -1328,8 +1328,6 @@ pub(crate) mod forward { spanned::Spanned as _, }; - use super::Spanning; - /// Representation of a `forward` attribute. /// /// ```rust,ignore @@ -1345,34 +1343,6 @@ pub(crate) mod forward { } } } - - impl Attribute { - /// Parses an [`Attribute`] from the provided [`syn::Attribute`]s, preserving its [`Span`]. - /// - /// [`Span`]: proc_macro2::Span - pub(crate) fn parse_attrs( - attrs: impl AsRef<[syn::Attribute]>, - attr_ident: &syn::Ident, - ) -> syn::Result>> { - attrs - .as_ref() - .iter() - .filter(|attr| attr.path().is_ident(attr_ident)) - .try_fold(None, |mut attrs, attr| { - let parsed = Spanning::new(attr.parse_args::()?, attr.span()); - if attrs.replace(parsed).is_some() { - Err(syn::Error::new( - attr.span(), - format!( - "only single `#[{attr_ident}(forward)]` attribute is allowed here", - ), - )) - } else { - Ok(attrs) - } - }) - } - } } #[cfg(any( From c8060cbd7f8afc2991cc7bb439eca682203417ac Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Tue, 22 Aug 2023 19:09:28 +0300 Subject: [PATCH 13/58] Fix typo, improve doc comments --- impl/src/as/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index d082c8d9..2bd2c894 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -276,7 +276,7 @@ enum FieldAttribute { /// /// ```rust,ignore /// #[as_ref(forward)] -/// #[as_ref()]x +/// #[as_ref()] /// ``` enum AsArgs { /// Blanket impl, fully forwarding to the field type @@ -341,7 +341,7 @@ impl FieldAttribute { }) } - /// Conversion into arguments on the attribute, if any + /// Extracts conversion arguments on the attribute, if any fn into_args(self) -> Option { match self { Self::Args(args) => Some(args), From 483f0c2708e634e929c092194521983fa3486bf2 Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 28 Aug 2023 13:33:54 +0300 Subject: [PATCH 14/58] Corrections --- CHANGELOG.md | 2 +- impl/src/as/mod.rs | 38 +++++++++++++++++++------------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb7eb9c3..846dc1a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,7 +59,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ([#279](https://github.com/JelteF/derive_more/pull/279)) - `derive_more::derive` module exporting only macros, without traits. ([#290](https://github.com/JelteF/derive_more/pull/290)) -- Add support for selective forwarding in the `AsRef`/`AsMut` derive +- Add support for selective forwarding to `AsRef`/`AsMut` derives. ([#298](https://github.com/JelteF/derive_more/pull/298)) ### Changed diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index 2bd2c894..661129d4 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -185,10 +185,9 @@ impl<'a> ToTokens for Expansion<'a> { ); let (trait_ident, method_ident, mut_) = &self.trait_info; - let ty_ident = &self.ident; - let (blanket, forward_impl, return_tys) = match &self.args { + let (is_blanket, is_forward, return_tys) = match &self.args { Some(AsArgs::Forward(_)) => { (true, true, Either::Left(std::iter::once(quote! { __AsT }))) } @@ -208,9 +207,9 @@ impl<'a> ToTokens for Expansion<'a> { .map(|return_ty| { let trait_ty = quote! { ::core::convert::#trait_ident <#return_ty> }; - let generics = if forward_impl { + let generics = if is_forward { let mut generics = self.generics.clone(); - if blanket { + if is_blanket { generics.params.push(parse_quote! { #return_ty }); generics .make_where_clause() @@ -231,7 +230,7 @@ impl<'a> ToTokens for Expansion<'a> { let (_, ty_gens, _) = self.generics.split_for_impl(); let mut body = quote! { & #mut_ self.#field_ident }; - if forward_impl { + if is_forward { body = quote! { <#field_ty as #trait_ty>::#method_ident(#body) }; } @@ -272,19 +271,6 @@ enum FieldAttribute { Skip(skip::Attribute), } -/// Arguments specifying which conversions should be generated -/// -/// ```rust,ignore -/// #[as_ref(forward)] -/// #[as_ref()] -/// ``` -enum AsArgs { - /// Blanket impl, fully forwarding to the field type - Forward(forward::Attribute), - /// Forward implementation, but only impl for specified types - Types(Punctuated), -} - impl Parse for FieldAttribute { fn parse(input: ParseStream<'_>) -> syn::Result { if input.is_empty() { @@ -341,7 +327,7 @@ impl FieldAttribute { }) } - /// Extracts conversion arguments on the attribute, if any + /// Extracts conversion arguments on the attribute, if any. fn into_args(self) -> Option { match self { Self::Args(args) => Some(args), @@ -350,6 +336,20 @@ impl FieldAttribute { } } +/// Arguments specifying which conversions should be generated. +/// +/// ```rust,ignore +/// #[as_ref(forward)] +/// #[as_ref()] +/// ``` +enum AsArgs { + /// Blanket impl, fully forwarding to the field type. + Forward(forward::Attribute), + + /// Forward implementation, but only impl for specified types. + Types(Punctuated), +} + impl Parse for AsArgs { fn parse(input: ParseStream) -> syn::Result { let ahead = input.fork(); From 7b28df148a9ba16e82783898473149d9bf7dfd6d Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 28 Aug 2023 13:34:00 +0300 Subject: [PATCH 15/58] Extend tests --- tests/as_mut.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++++++ tests/as_ref.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) diff --git a/tests/as_mut.rs b/tests/as_mut.rs index 982a0f4b..4e677ec2 100644 --- a/tests/as_mut.rs +++ b/tests/as_mut.rs @@ -78,6 +78,14 @@ mod single_field { } } + // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsMut for Types { + fn as_mut(&mut self) -> &mut Foo { + &mut self.0 + } + } + #[test] fn types() { let mut item = Types(Foo(1, 2.0, false)); @@ -100,6 +108,14 @@ mod single_field { } } + // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsMut for FieldTypes { + fn as_mut(&mut self) -> &mut Foo { + &mut self.0 + } + } + #[test] fn field_types() { let mut item = FieldTypes(Foo(1, 2.0, false)); @@ -282,6 +298,14 @@ mod single_field { } } + // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsMut for Types { + fn as_mut(&mut self) -> &mut Foo { + &mut self.first + } + } + #[test] fn types() { let mut item = Types { @@ -309,6 +333,14 @@ mod single_field { } } + // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsMut for FieldTypes { + fn as_mut(&mut self) -> &mut Foo { + &mut self.first + } + } + #[test] fn field_types() { let mut item = FieldTypes { @@ -512,6 +544,22 @@ mod multi_field { #[derive(AsMut)] struct Types(#[as_mut(str)] String, #[as_mut([u8])] Vec); + // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsMut for Types { + fn as_mut(&mut self) -> &mut String { + &mut self.0 + } + } + + // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsMut> for Types { + fn as_mut(&mut self) -> &mut Vec { + &mut self.1 + } + } + #[test] fn types() { let mut item = Types("test".to_owned(), vec![0]); @@ -689,6 +737,22 @@ mod multi_field { second: Vec, } + // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsMut for Types { + fn as_mut(&mut self) -> &mut String { + &mut self.first + } + } + + // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsMut> for Types { + fn as_mut(&mut self) -> &mut Vec { + &mut self.second + } + } + #[test] fn types() { let mut item = Types { diff --git a/tests/as_ref.rs b/tests/as_ref.rs index 22d8c707..2a0e4fe4 100644 --- a/tests/as_ref.rs +++ b/tests/as_ref.rs @@ -78,6 +78,14 @@ mod single_field { } } + // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsRef for Types { + fn as_ref(&self) -> &Foo { + &self.0 + } + } + #[test] fn types() { let item = Types(Foo(1, 2.0, false)); @@ -100,6 +108,14 @@ mod single_field { } } + // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsRef for FieldTypes { + fn as_ref(&self) -> &Foo { + &self.0 + } + } + #[test] fn field_types() { let item = FieldTypes(Foo(1, 2.0, false)); @@ -282,6 +298,14 @@ mod single_field { } } + // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsRef for Types { + fn as_ref(&self) -> &Foo { + &self.first + } + } + #[test] fn types() { let item = Types { @@ -309,6 +333,14 @@ mod single_field { } } + // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsRef for FieldTypes { + fn as_ref(&self) -> &Foo { + &self.first + } + } + #[test] fn field_types() { let item = FieldTypes { @@ -512,6 +544,22 @@ mod multi_field { #[derive(AsRef)] struct Types(#[as_ref(str)] String, #[as_ref([u8])] Vec); + // Asserts that the macro expansion doesn't generate `AsRef` impl for the third field, by + // producing trait implementations conflict error during compilation, if it does. + impl AsRef for Types { + fn as_ref(&self) -> &String { + &self.0 + } + } + + // Asserts that the macro expansion doesn't generate `AsRef` impl for the third field, by + // producing trait implementations conflict error during compilation, if it does. + impl AsRef> for Types { + fn as_ref(&self) -> &Vec { + &self.1 + } + } + #[test] fn types() { let item = Types("test".to_owned(), vec![0]); @@ -689,6 +737,22 @@ mod multi_field { second: Vec, } + // Asserts that the macro expansion doesn't generate `AsRef` impl for the third field, by + // producing trait implementations conflict error during compilation, if it does. + impl AsRef for Types { + fn as_ref(&self) -> &String { + &self.first + } + } + + // Asserts that the macro expansion doesn't generate `AsRef` impl for the third field, by + // producing trait implementations conflict error during compilation, if it does. + impl AsRef> for Types { + fn as_ref(&self) -> &Vec { + &self.second + } + } + #[test] fn types() { let item = Types { From 00227e6a5d778f0aab1bf5858cae542d0d25df5c Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 28 Aug 2023 13:46:02 +0300 Subject: [PATCH 16/58] Add test cases covering direct explicit impl --- tests/as_ref.rs | 155 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 137 insertions(+), 18 deletions(-) diff --git a/tests/as_ref.rs b/tests/as_ref.rs index 2a0e4fe4..c0679b3a 100644 --- a/tests/as_ref.rs +++ b/tests/as_ref.rs @@ -97,6 +97,37 @@ mod single_field { assert!(ptr::eq(rf, item.0.as_ref())); } + #[derive(AsRef)] + #[as_ref(i32, Foo)] + struct TypesWithInner(Foo); + + // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsRef for TypesWithInner { + fn as_ref(&self) -> &bool { + self.0.as_ref() + } + } + + // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsRef for TypesWithInner { + fn as_ref(&self) -> &f64 { + self.0.as_ref() + } + } + + #[test] + fn types_with_inner() { + let item = TypesWithInner(Foo(1, 2.0, false)); + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.0.as_ref())); + + let rf: &Foo = item.as_ref(); + assert!(ptr::eq(rf, &item.0)); + } + #[derive(AsRef)] struct FieldTypes(#[as_ref(i32, f64)] Foo); @@ -127,6 +158,38 @@ mod single_field { assert!(ptr::eq(rf, item.0.as_ref())); } + type RenamedFoo = Foo; + + #[derive(AsRef)] + struct FieldTypesWithRenamedInner(#[as_ref(i32, RenamedFoo)] Foo); + + // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsRef for FieldTypesWithRenamedInner { + fn as_ref(&self) -> &bool { + self.0.as_ref() + } + } + + // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsRef for FieldTypesWithRenamedInner { + fn as_ref(&self) -> &f64 { + self.0.as_ref() + } + } + + #[test] + fn field_types_with_renamed_inner() { + let item = FieldTypesWithRenamedInner(Foo(1, 2.0, false)); + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.0.as_ref())); + + let rf: &Foo = item.as_ref(); + assert!(ptr::eq(rf, &item.0)); + } + mod generic { use super::*; @@ -319,6 +382,41 @@ mod single_field { assert!(ptr::eq(rf, item.first.as_ref())); } + #[derive(AsRef)] + #[as_ref(i32, Foo)] + struct TypesWithInner { + first: Foo, + } + + // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsRef for TypesWithInner { + fn as_ref(&self) -> &bool { + self.first.as_ref() + } + } + + // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsRef for TypesWithInner { + fn as_ref(&self) -> &f64 { + self.first.as_ref() + } + } + + #[test] + fn types_with_inner() { + let item = TypesWithInner { + first: Foo(1, 2.0, false), + }; + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.first.as_ref())); + + let rf: &Foo = item.as_ref(); + assert!(ptr::eq(rf, &item.first)); + } + #[derive(AsRef)] struct FieldTypes { #[as_ref(i32, f64)] @@ -354,6 +452,43 @@ mod single_field { assert!(ptr::eq(rf, item.first.as_ref())); } + type RenamedFoo = Foo; + + #[derive(AsRef)] + struct FieldTypesWithRenamedInner { + #[as_ref(i32, RenamedFoo)] + first: Foo, + } + + // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsRef for FieldTypesWithRenamedInner { + fn as_ref(&self) -> &bool { + self.first.as_ref() + } + } + + // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by + // producing trait implementations conflict error during compilation, if it does. + impl AsRef for FieldTypesWithRenamedInner { + fn as_ref(&self) -> &f64 { + self.first.as_ref() + } + } + + #[test] + fn field_types_with_renamed_inner() { + let item = FieldTypesWithRenamedInner { + first: Foo(1, 2.0, false), + }; + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.first.as_ref())); + + let rf: &Foo = item.as_ref(); + assert!(ptr::eq(rf, &item.first)); + } + mod generic { use super::*; @@ -542,15 +677,7 @@ mod multi_field { } #[derive(AsRef)] - struct Types(#[as_ref(str)] String, #[as_ref([u8])] Vec); - - // Asserts that the macro expansion doesn't generate `AsRef` impl for the third field, by - // producing trait implementations conflict error during compilation, if it does. - impl AsRef for Types { - fn as_ref(&self) -> &String { - &self.0 - } - } + struct Types(#[as_ref(str, String)] String, #[as_ref([u8])] Vec); // Asserts that the macro expansion doesn't generate `AsRef` impl for the third field, by // producing trait implementations conflict error during compilation, if it does. @@ -731,20 +858,12 @@ mod multi_field { #[derive(AsRef)] struct Types { - #[as_ref(str)] + #[as_ref(str, String)] first: String, #[as_ref([u8])] second: Vec, } - // Asserts that the macro expansion doesn't generate `AsRef` impl for the third field, by - // producing trait implementations conflict error during compilation, if it does. - impl AsRef for Types { - fn as_ref(&self) -> &String { - &self.first - } - } - // Asserts that the macro expansion doesn't generate `AsRef` impl for the third field, by // producing trait implementations conflict error during compilation, if it does. impl AsRef> for Types { From 1635289be4cfd715044114d15f5aa1e6e8ae23c0 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Tue, 29 Aug 2023 12:11:56 +0300 Subject: [PATCH 17/58] Update comments in tests --- tests/as_mut.rs | 88 ++++++++++++++++++------------------ tests/as_ref.rs | 116 +++++++++++++++++++++--------------------------- 2 files changed, 95 insertions(+), 109 deletions(-) diff --git a/tests/as_mut.rs b/tests/as_mut.rs index 4e677ec2..d3c6be8a 100644 --- a/tests/as_mut.rs +++ b/tests/as_mut.rs @@ -70,16 +70,18 @@ mod single_field { #[as_mut(i32, f64)] struct Types(Foo); - // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. impl AsMut for Types { fn as_mut(&mut self) -> &mut bool { self.0.as_mut() } } - // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate an `AsMut` impl for + // the field type, by producing a trait implementations conflict error + // during compilation, if it does. impl AsMut for Types { fn as_mut(&mut self) -> &mut Foo { &mut self.0 @@ -100,16 +102,18 @@ mod single_field { #[derive(AsMut)] struct FieldTypes(#[as_mut(i32, f64)] Foo); - // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. impl AsMut for FieldTypes { fn as_mut(&mut self) -> &mut bool { self.0.as_mut() } } - // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate an `AsMut` impl for + // the field type, by producing a trait implementations conflict error + // during compilation, if it does. impl AsMut for FieldTypes { fn as_mut(&mut self) -> &mut Foo { &mut self.0 @@ -177,8 +181,9 @@ mod single_field { #[as_mut(i32, f64)] struct Types(T); - // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. impl> AsMut for Types { fn as_mut(&mut self) -> &mut bool { self.0.as_mut() @@ -199,8 +204,9 @@ mod single_field { #[derive(AsMut)] struct FieldTypes(#[as_mut(i32, f64)] T); - // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. impl> AsMut for FieldTypes { fn as_mut(&mut self) -> &mut bool { self.0.as_mut() @@ -290,16 +296,18 @@ mod single_field { first: Foo, } - // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. impl AsMut for Types { fn as_mut(&mut self) -> &mut bool { self.first.as_mut() } } - // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate an `AsMut` impl for + // the field type, by producing a trait implementations conflict error + // during compilation, if it does. impl AsMut for Types { fn as_mut(&mut self) -> &mut Foo { &mut self.first @@ -325,16 +333,18 @@ mod single_field { first: Foo, } - // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. impl AsMut for FieldTypes { fn as_mut(&mut self) -> &mut bool { self.first.as_mut() } } - // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate an `AsMut` impl for + // the field type, by producing a trait implementations conflict error + // during compilation, if it does. impl AsMut for FieldTypes { fn as_mut(&mut self) -> &mut Foo { &mut self.first @@ -424,8 +434,9 @@ mod single_field { first: T, } - // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. impl> AsMut for Types { fn as_mut(&mut self) -> &mut bool { self.first.as_mut() @@ -451,8 +462,9 @@ mod single_field { first: T, } - // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. impl> AsMut for FieldTypes { fn as_mut(&mut self) -> &mut bool { self.first.as_mut() @@ -542,17 +554,9 @@ mod multi_field { } #[derive(AsMut)] - struct Types(#[as_mut(str)] String, #[as_mut([u8])] Vec); + struct Types(#[as_mut(str, String)] String, #[as_mut([u8])] Vec); - // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. - impl AsMut for Types { - fn as_mut(&mut self) -> &mut String { - &mut self.0 - } - } - - // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by + // Asserts that the macro expansion doesn't generate `AsMut` impl for the field type, by // producing trait implementations conflict error during compilation, if it does. impl AsMut> for Types { fn as_mut(&mut self) -> &mut Vec { @@ -567,6 +571,9 @@ mod multi_field { let rf: &mut str = item.as_mut(); assert!(ptr::eq(rf, item.0.as_mut())); + let rf: &mut String = item.as_mut(); + assert!(ptr::eq(rf, &mut item.0)); + let rf: &mut [u8] = item.as_mut(); assert!(ptr::eq(rf, item.1.as_mut())); } @@ -731,20 +738,12 @@ mod multi_field { #[derive(AsMut)] struct Types { - #[as_mut(str)] + #[as_mut(str, String)] first: String, #[as_mut([u8])] second: Vec, } - // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. - impl AsMut for Types { - fn as_mut(&mut self) -> &mut String { - &mut self.first - } - } - // Asserts that the macro expansion doesn't generate `AsMut` impl for unmentioned type, by // producing trait implementations conflict error during compilation, if it does. impl AsMut> for Types { @@ -763,6 +762,9 @@ mod multi_field { let rf: &mut str = item.as_mut(); assert!(ptr::eq(rf, item.first.as_mut())); + let rf: &mut String = item.as_mut(); + assert!(ptr::eq(rf, &mut item.first)); + let rf: &mut [u8] = item.as_mut(); assert!(ptr::eq(rf, item.second.as_mut())); } diff --git a/tests/as_ref.rs b/tests/as_ref.rs index c0679b3a..405bc688 100644 --- a/tests/as_ref.rs +++ b/tests/as_ref.rs @@ -70,16 +70,18 @@ mod single_field { #[as_ref(i32, f64)] struct Types(Foo); - // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` + // impl forwarding to the field type, by producing atrait implementations + // conflict error during compilation, if it does. impl AsRef for Types { fn as_ref(&self) -> &bool { self.0.as_ref() } } - // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate an `AsRef` impl for + // the field type, by producing a trait implementations conflict error + // during compilation, if it does. impl AsRef for Types { fn as_ref(&self) -> &Foo { &self.0 @@ -101,22 +103,15 @@ mod single_field { #[as_ref(i32, Foo)] struct TypesWithInner(Foo); - // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. impl AsRef for TypesWithInner { fn as_ref(&self) -> &bool { self.0.as_ref() } } - // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. - impl AsRef for TypesWithInner { - fn as_ref(&self) -> &f64 { - self.0.as_ref() - } - } - #[test] fn types_with_inner() { let item = TypesWithInner(Foo(1, 2.0, false)); @@ -131,16 +126,18 @@ mod single_field { #[derive(AsRef)] struct FieldTypes(#[as_ref(i32, f64)] Foo); - // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. impl AsRef for FieldTypes { fn as_ref(&self) -> &bool { self.0.as_ref() } } - // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate an `AsRef` impl for + // the field type, by producing a trait implementations conflict error + // during compilation, if it does. impl AsRef for FieldTypes { fn as_ref(&self) -> &Foo { &self.0 @@ -163,22 +160,15 @@ mod single_field { #[derive(AsRef)] struct FieldTypesWithRenamedInner(#[as_ref(i32, RenamedFoo)] Foo); - // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. impl AsRef for FieldTypesWithRenamedInner { fn as_ref(&self) -> &bool { self.0.as_ref() } } - // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. - impl AsRef for FieldTypesWithRenamedInner { - fn as_ref(&self) -> &f64 { - self.0.as_ref() - } - } - #[test] fn field_types_with_renamed_inner() { let item = FieldTypesWithRenamedInner(Foo(1, 2.0, false)); @@ -240,8 +230,9 @@ mod single_field { #[as_ref(i32, f64)] struct Types(T); - // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. impl> AsRef for Types { fn as_ref(&self) -> &bool { self.0.as_ref() @@ -262,8 +253,9 @@ mod single_field { #[derive(AsRef)] struct FieldTypes(#[as_ref(i32, f64)] T); - // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. impl> AsRef for FieldTypes { fn as_ref(&self) -> &bool { self.0.as_ref() @@ -353,16 +345,18 @@ mod single_field { first: Foo, } - // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. impl AsRef for Types { fn as_ref(&self) -> &bool { self.first.as_ref() } } - // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate an `AsRef` impl for + // the field type, by producing a trait implementations conflict error + // during compilation, if it does. impl AsRef for Types { fn as_ref(&self) -> &Foo { &self.first @@ -388,22 +382,15 @@ mod single_field { first: Foo, } - // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. impl AsRef for TypesWithInner { fn as_ref(&self) -> &bool { self.first.as_ref() } } - // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. - impl AsRef for TypesWithInner { - fn as_ref(&self) -> &f64 { - self.first.as_ref() - } - } - #[test] fn types_with_inner() { let item = TypesWithInner { @@ -423,16 +410,18 @@ mod single_field { first: Foo, } - // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. impl AsRef for FieldTypes { fn as_ref(&self) -> &bool { self.first.as_ref() } } - // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate an `AsRef` impl for + // the field type, by producing a trait implementations conflict error + // during compilation, if it does. impl AsRef for FieldTypes { fn as_ref(&self) -> &Foo { &self.first @@ -460,22 +449,15 @@ mod single_field { first: Foo, } - // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. impl AsRef for FieldTypesWithRenamedInner { fn as_ref(&self) -> &bool { self.first.as_ref() } } - // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. - impl AsRef for FieldTypesWithRenamedInner { - fn as_ref(&self) -> &f64 { - self.first.as_ref() - } - } - #[test] fn field_types_with_renamed_inner() { let item = FieldTypesWithRenamedInner { @@ -559,8 +541,9 @@ mod single_field { first: T, } - // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. impl> AsRef for Types { fn as_ref(&self) -> &bool { self.first.as_ref() @@ -586,8 +569,9 @@ mod single_field { first: T, } - // Asserts that the macro expansion doesn't generate `AsRef` impl for unmentioned type, by - // producing trait implementations conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. impl> AsRef for FieldTypes { fn as_ref(&self) -> &bool { self.first.as_ref() @@ -679,7 +663,7 @@ mod multi_field { #[derive(AsRef)] struct Types(#[as_ref(str, String)] String, #[as_ref([u8])] Vec); - // Asserts that the macro expansion doesn't generate `AsRef` impl for the third field, by + // Asserts that the macro expansion doesn't generate `AsRef` impl for the field type, by // producing trait implementations conflict error during compilation, if it does. impl AsRef> for Types { fn as_ref(&self) -> &Vec { @@ -864,7 +848,7 @@ mod multi_field { second: Vec, } - // Asserts that the macro expansion doesn't generate `AsRef` impl for the third field, by + // Asserts that the macro expansion doesn't generate `AsRef` impl for the field type, by // producing trait implementations conflict error during compilation, if it does. impl AsRef> for Types { fn as_ref(&self) -> &Vec { From c2e6dc8444c08c1d12b47b3c50deaff794b81a1e Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Tue, 29 Aug 2023 12:29:20 +0300 Subject: [PATCH 18/58] Add note about tuple conversions to docs --- impl/doc/as_mut.md | 20 +++++++++++++++++--- impl/doc/as_ref.md | 21 ++++++++++++++++++--- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/impl/doc/as_mut.md b/impl/doc/as_mut.md index e9c7bb51..0b194d11 100644 --- a/impl/doc/as_mut.md +++ b/impl/doc/as_mut.md @@ -105,7 +105,7 @@ An implementation will be generated for each indicated field. # #[derive(AsMut)] struct MyWrapper { - #[as_mut] + #[as_mut(str)] name: String, #[as_mut] num: i32, @@ -121,9 +121,9 @@ Generates: # num: i32, # valid: bool, # } -impl AsMut for MyWrapper { +impl AsMut for MyWrapper { fn as_mut(&mut self) -> &mut String { - &mut self.name + >::as_mut(&mut self.name) } } @@ -134,6 +134,20 @@ impl AsMut for MyWrapper { } ``` +Only conversions that use a single field are possible with this derive. +Something like this wouldn't work, due to the nature of the `AsMut` trait: + +```rust,compile_fail +# use derive_more::AsRef +# +// Error! `#[as_ref(...)]` attribute can only be placed on structs with exactly one field +#[derive(AsRef)] +#[as_ref((str, [u8]))] +struct MyWrapper(String, Vec) +``` + +If you need to convert into multiple references, consider using the +[`Into`](crate::Into) derive with `#[into(ref_mut)]`. ### Skipping diff --git a/impl/doc/as_ref.md b/impl/doc/as_ref.md index f9352841..edbe38a1 100644 --- a/impl/doc/as_ref.md +++ b/impl/doc/as_ref.md @@ -106,7 +106,7 @@ An implementation will be generated for each indicated field. # #[derive(AsRef)] struct MyWrapper { - #[as_ref] + #[as_ref(str)] name: String, #[as_ref] num: i32, @@ -122,9 +122,9 @@ Generates: # num: i32, # valid: bool, # } -impl AsRef for MyWrapper { +impl AsRef for MyWrapper { fn as_ref(&self) -> &String { - &self.name + >::as_ref(&self.name) } } @@ -135,6 +135,21 @@ impl AsRef for MyWrapper { } ``` +Only conversions that use a single field are possible with this derive. +Something like this wouldn't work, due to the nature of the `AsRef` trait: + +```rust,compile_fail +# use derive_more::AsRef +# +// Error! `#[as_ref(...)]` attribute can only be placed on structs with exactly one field +#[derive(AsRef)] +#[as_ref((str, [u8]))] +struct MyWrapper(String, Vec) +``` + +If you need to convert into multiple references, consider using the +[`Into`](crate::Into) derive with `#[into(ref)]`. + ### Skipping From 0adc51c1b4fb1b65054ecf125975e7ff62ed13ac Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Tue, 29 Aug 2023 12:33:38 +0300 Subject: [PATCH 19/58] [skip ci] Add compile_fail tests for case with tuple type in struct attribute --- tests/compile_fail/as_mut/struct_attr_tuple.rs | 8 ++++++++ tests/compile_fail/as_mut/struct_attr_tuple.stderr | 9 +++++++++ tests/compile_fail/as_ref/struct_attr_tuple.rs | 8 ++++++++ tests/compile_fail/as_ref/struct_attr_tuple.stderr | 9 +++++++++ 4 files changed, 34 insertions(+) create mode 100644 tests/compile_fail/as_mut/struct_attr_tuple.rs create mode 100644 tests/compile_fail/as_mut/struct_attr_tuple.stderr create mode 100644 tests/compile_fail/as_ref/struct_attr_tuple.rs create mode 100644 tests/compile_fail/as_ref/struct_attr_tuple.stderr diff --git a/tests/compile_fail/as_mut/struct_attr_tuple.rs b/tests/compile_fail/as_mut/struct_attr_tuple.rs new file mode 100644 index 00000000..17eea749 --- /dev/null +++ b/tests/compile_fail/as_mut/struct_attr_tuple.rs @@ -0,0 +1,8 @@ +#[derive(derive_more::AsMut)] +#[as_mut((i32, f32))] +struct Foo { + bar: i32, + baz: f32, +} + +fn main() {} diff --git a/tests/compile_fail/as_mut/struct_attr_tuple.stderr b/tests/compile_fail/as_mut/struct_attr_tuple.stderr new file mode 100644 index 00000000..b961c8a6 --- /dev/null +++ b/tests/compile_fail/as_mut/struct_attr_tuple.stderr @@ -0,0 +1,9 @@ +error: `#[as_mut(...)]` attribute can only be placed on structs with exactly one field + --> tests/compile_fail/as_mut/struct_attr_tuple.rs:3:12 + | +3 | struct Foo { + | ____________^ +4 | | bar: i32, +5 | | baz: f32, +6 | | } + | |_^ diff --git a/tests/compile_fail/as_ref/struct_attr_tuple.rs b/tests/compile_fail/as_ref/struct_attr_tuple.rs new file mode 100644 index 00000000..c7aa01cb --- /dev/null +++ b/tests/compile_fail/as_ref/struct_attr_tuple.rs @@ -0,0 +1,8 @@ +#[derive(derive_more::AsRef)] +#[as_ref((i32, f32))] +struct Foo { + bar: i32, + baz: f32, +} + +fn main() {} diff --git a/tests/compile_fail/as_ref/struct_attr_tuple.stderr b/tests/compile_fail/as_ref/struct_attr_tuple.stderr new file mode 100644 index 00000000..25d2d941 --- /dev/null +++ b/tests/compile_fail/as_ref/struct_attr_tuple.stderr @@ -0,0 +1,9 @@ +error: `#[as_ref(...)]` attribute can only be placed on structs with exactly one field + --> tests/compile_fail/as_ref/struct_attr_tuple.rs:3:12 + | +3 | struct Foo { + | ____________^ +4 | | bar: i32, +5 | | baz: f32, +6 | | } + | |_^ From 99e993c2fd4a27634fcc77a9c0fc0b3c7542f29f Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Wed, 30 Aug 2023 17:43:24 +0300 Subject: [PATCH 20/58] Add specialization machinery --- src/as.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 6 ++++++ 2 files changed, 52 insertions(+) create mode 100644 src/as.rs diff --git a/src/as.rs b/src/as.rs new file mode 100644 index 00000000..0e5ed9b7 --- /dev/null +++ b/src/as.rs @@ -0,0 +1,46 @@ +use core::marker::PhantomData; + +pub struct Conv(PhantomData<(Frm, To)>); + +pub trait GetRef { + type Frm; + type To; + + fn get_ref(&self, frm: Self::Frm) -> Self::To; +} + +impl<'a, T> GetRef for &Conv<&'a T, T> { + type Frm = &'a T; + type To = &'a T; + + fn get_ref(&self, frm: Self::Frm) -> Self::To { + frm + } +} + +impl<'a, Frm: AsRef, To: 'a> GetRef for Conv<&'a Frm, To> { + type Frm = &'a Frm; + type To = &'a To; + + fn get_ref(&self, frm: Self::Frm) -> Self::To { + frm.as_ref() + } +} + +impl<'a, T> GetRef for &Conv<&'a mut T, T> { + type Frm = &'a mut T; + type To = &'a mut T; + + fn get_ref(&self, frm: Self::Frm) -> Self::To { + frm + } +} + +impl<'a, Frm: AsMut, To: 'a> GetRef for Conv<&'a mut Frm, To> { + type Frm = &'a mut Frm; + type To = &'a mut To; + + fn get_ref(&self, frm: Self::Frm) -> Self::To { + frm.as_mut() + } +} diff --git a/src/lib.rs b/src/lib.rs index e38053a8..20b2cc56 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,6 +55,9 @@ pub mod __private { #[cfg(feature = "error")] pub use crate::vendor::thiserror::aserror::AsDynError; + + #[cfg(feature = "as_ref")] + pub use crate::r#as::{Conv, GetRef}; } /// Module containing macro definitions only, without corresponding traits. @@ -77,6 +80,9 @@ mod ops; #[cfg(any(feature = "add", feature = "not"))] pub use crate::ops::UnitError; +#[cfg(feature = "as_ref")] +mod r#as; + #[cfg(feature = "debug")] mod fmt; From f96ac444aa86562a1da7b89f2219526aaf8f4a07 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Wed, 30 Aug 2023 17:53:27 +0300 Subject: [PATCH 21/58] Switch to using syn::Type instead of polyfill --- impl/src/as/mod.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index 661129d4..f583fe65 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -15,10 +15,7 @@ use syn::{ Token, }; -use crate::{ - parsing::Type, - utils::{forward, skip, Either, Spanning}, -}; +use crate::utils::{forward, skip, Either, Spanning}; /// Expands an [`AsRef`]/[`AsMut`] derive macro. pub fn expand( @@ -347,7 +344,7 @@ enum AsArgs { Forward(forward::Attribute), /// Forward implementation, but only impl for specified types. - Types(Punctuated), + Types(Punctuated), } impl Parse for AsArgs { @@ -359,7 +356,7 @@ impl Parse for AsArgs { } input - .parse_terminated(Type::parse, Token![,]) + .parse_terminated(syn::Type::parse, Token![,]) .map(Self::Types) } } From 4714689659955e47a46ce53a5bb0452faca3f5fa Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Wed, 30 Aug 2023 18:28:02 +0300 Subject: [PATCH 22/58] Add type search util --- impl/Cargo.toml | 2 +- impl/src/utils.rs | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/impl/Cargo.toml b/impl/Cargo.toml index 036b8499..8fa0f8f6 100644 --- a/impl/Cargo.toml +++ b/impl/Cargo.toml @@ -48,7 +48,7 @@ default = [] add = [] add_assign = [] -as_ref = ["syn/extra-traits"] +as_ref = ["syn/extra-traits", "syn/visit"] constructor = [] debug = ["syn/extra-traits", "dep:unicode-xid"] deref = [] diff --git a/impl/src/utils.rs b/impl/src/utils.rs index c8ab138b..09e13d90 100644 --- a/impl/src/utils.rs +++ b/impl/src/utils.rs @@ -1681,3 +1681,41 @@ mod fields_ext { impl FieldsExt for T {} } + +#[cfg(feature = "as_ref")] +mod type_search { + use syn::visit::Visit; + + use super::HashSet; + + struct TypeSearchVisitor<'a> { + search: &'a HashSet<&'a syn::Ident>, + found: bool, + } + + impl<'a, 'ast> Visit<'ast> for TypeSearchVisitor<'a> { + fn visit_type(&mut self, ty: &'ast syn::Type) { + if self.found { + return; + } + + if let syn::Type::Path(syn::TypePath { path, .. }) = ty { + self.found = path + .get_ident() + .map_or(false, |ident| self.search.contains(ident)); + } + + syn::visit::visit_type(self, ty); + } + } + + pub fn contains_any_of(ty: &syn::Type, search: &HashSet<&syn::Ident>) -> bool { + let mut visitor = TypeSearchVisitor { + search, + found: false, + }; + + visitor.visit_type(ty); + visitor.found + } +} From 210e970d569cfe34741ccc698c67defbe0e043dc Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Wed, 30 Aug 2023 18:59:05 +0300 Subject: [PATCH 23/58] Fix sized bounds, improve naming --- src/as.rs | 30 +++++++++++++++++++----------- src/lib.rs | 2 +- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/as.rs b/src/as.rs index 0e5ed9b7..e31711b9 100644 --- a/src/as.rs +++ b/src/as.rs @@ -1,46 +1,54 @@ use core::marker::PhantomData; -pub struct Conv(PhantomData<(Frm, To)>); +pub struct Conv(PhantomData<(Box, Box)>); -pub trait GetRef { +impl Default for Conv { + fn default() -> Self { + Self(PhantomData) + } +} + +pub trait ExtractRef { type Frm; type To; - fn get_ref(&self, frm: Self::Frm) -> Self::To; + fn __extract_ref(&self, frm: Self::Frm) -> Self::To; } -impl<'a, T> GetRef for &Conv<&'a T, T> { +impl<'a, T: ?Sized> ExtractRef for &Conv<&'a T, T> { type Frm = &'a T; type To = &'a T; - fn get_ref(&self, frm: Self::Frm) -> Self::To { + fn __extract_ref(&self, frm: Self::Frm) -> Self::To { frm } } -impl<'a, Frm: AsRef, To: 'a> GetRef for Conv<&'a Frm, To> { +impl<'a, Frm: ?Sized + AsRef, To: 'a + ?Sized> ExtractRef for Conv<&'a Frm, To> { type Frm = &'a Frm; type To = &'a To; - fn get_ref(&self, frm: Self::Frm) -> Self::To { + fn __extract_ref(&self, frm: Self::Frm) -> Self::To { frm.as_ref() } } -impl<'a, T> GetRef for &Conv<&'a mut T, T> { +impl<'a, T: ?Sized> ExtractRef for &Conv<&'a mut T, T> { type Frm = &'a mut T; type To = &'a mut T; - fn get_ref(&self, frm: Self::Frm) -> Self::To { + fn __extract_ref(&self, frm: Self::Frm) -> Self::To { frm } } -impl<'a, Frm: AsMut, To: 'a> GetRef for Conv<&'a mut Frm, To> { +impl<'a, Frm: ?Sized + AsMut, To: ?Sized + 'a> ExtractRef + for Conv<&'a mut Frm, To> +{ type Frm = &'a mut Frm; type To = &'a mut To; - fn get_ref(&self, frm: Self::Frm) -> Self::To { + fn __extract_ref(&self, frm: Self::Frm) -> Self::To { frm.as_mut() } } diff --git a/src/lib.rs b/src/lib.rs index 20b2cc56..e5b26f8c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,7 +57,7 @@ pub mod __private { pub use crate::vendor::thiserror::aserror::AsDynError; #[cfg(feature = "as_ref")] - pub use crate::r#as::{Conv, GetRef}; + pub use crate::r#as::{Conv, ExtractRef}; } /// Module containing macro definitions only, without corresponding traits. From 06a425cb60bd852902c8653d7bea02a3315cb66e Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Wed, 30 Aug 2023 19:00:17 +0300 Subject: [PATCH 24/58] Add specialization handling to derive --- impl/src/as/mod.rs | 55 +++++++++++++++++++++++++++++++++++----------- impl/src/utils.rs | 8 ++++++- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index f583fe65..ec211dc3 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -15,7 +15,7 @@ use syn::{ Token, }; -use crate::utils::{forward, skip, Either, Spanning}; +use crate::utils::{contains_any_of, forward, skip, Either, HashSet, Spanning}; /// Expands an [`AsRef`]/[`AsMut`] derive macro. pub fn expand( @@ -185,23 +185,38 @@ impl<'a> ToTokens for Expansion<'a> { let ty_ident = &self.ident; let (is_blanket, is_forward, return_tys) = match &self.args { - Some(AsArgs::Forward(_)) => { - (true, true, Either::Left(std::iter::once(quote! { __AsT }))) - } - Some(AsArgs::Types(tys)) => ( - false, + Some(AsArgs::Forward(_)) => ( true, - Either::Right(tys.iter().map(ToTokens::into_token_stream)), + true, + Either::Left(std::iter::once(Cow::Owned(parse_quote! { __AsT }))), ), + Some(AsArgs::Types(tys)) => { + (false, true, Either::Right(tys.iter().map(Cow::Borrowed))) + } None => ( false, false, - Either::Left(std::iter::once(quote! { #field_ty })), + Either::Left(std::iter::once(Cow::Borrowed(field_ty))), ), }; + let param_idents: HashSet<_> = self + .generics + .type_params() + .map(|param| ¶m.ident) + .collect(); + + let field_contains_param = contains_any_of(&self.field.ty, ¶m_idents); + return_tys .map(|return_ty| { + let contains_param = + field_contains_param || contains_any_of(&return_ty, ¶m_idents); + + let is_field_type = &self.field.ty == return_ty.as_ref(); + + let is_forward = is_forward && !is_field_type; + let trait_ty = quote! { ::core::convert::#trait_ident <#return_ty> }; let generics = if is_forward { @@ -213,10 +228,12 @@ impl<'a> ToTokens for Expansion<'a> { .predicates .push(parse_quote! { #return_ty: ?::core::marker::Sized }); } - generics - .make_where_clause() - .predicates - .push(parse_quote! { #field_ty: #trait_ty }); + if contains_param { + generics + .make_where_clause() + .predicates + .push(parse_quote! { #field_ty: #trait_ty }); + } Cow::Owned(generics) } else { @@ -228,7 +245,19 @@ impl<'a> ToTokens for Expansion<'a> { let mut body = quote! { & #mut_ self.#field_ident }; if is_forward { - body = quote! { <#field_ty as #trait_ty>::#method_ident(#body) }; + if contains_param { + body = quote! { + <#field_ty as #trait_ty>::#method_ident(#body) + }; + } else { + body = quote! { + use ::derive_more::__private::ExtractRef as _; + + let conv: ::derive_more::__private::Conv<& #mut_ #field_ty, #return_ty> + = Default::default(); + (&&conv).__extract_ref(#body) + }; + } } quote! { diff --git a/impl/src/utils.rs b/impl/src/utils.rs index 09e13d90..a70c8d1a 100644 --- a/impl/src/utils.rs +++ b/impl/src/utils.rs @@ -31,6 +31,9 @@ pub(crate) use self::fields_ext::FieldsExt; ))] pub(crate) use self::spanning::Spanning; +#[cfg(feature = "as_ref")] +pub(crate) use self::type_search::contains_any_of; + #[derive(Clone, Copy, Default)] pub struct DeterministicState; @@ -1709,7 +1712,10 @@ mod type_search { } } - pub fn contains_any_of(ty: &syn::Type, search: &HashSet<&syn::Ident>) -> bool { + pub(crate) fn contains_any_of( + ty: &syn::Type, + search: &HashSet<&syn::Ident>, + ) -> bool { let mut visitor = TypeSearchVisitor { search, found: false, From 4a26f1a4881ead65d5f9e3f67116cba9d4a92db9 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Wed, 30 Aug 2023 19:02:49 +0300 Subject: [PATCH 25/58] Fix blanket impl --- impl/src/as/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index ec211dc3..e22e32e4 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -228,7 +228,7 @@ impl<'a> ToTokens for Expansion<'a> { .predicates .push(parse_quote! { #return_ty: ?::core::marker::Sized }); } - if contains_param { + if is_blanket || contains_param { generics .make_where_clause() .predicates @@ -245,7 +245,7 @@ impl<'a> ToTokens for Expansion<'a> { let mut body = quote! { & #mut_ self.#field_ident }; if is_forward { - if contains_param { + if is_blanket || contains_param { body = quote! { <#field_ty as #trait_ty>::#method_ident(#body) }; From 990dee8c1a20fcffc70fab6db0be4ea284f59825 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Wed, 30 Aug 2023 20:21:22 +0300 Subject: [PATCH 26/58] Add new test cases --- tests/as_mut.rs | 177 ++++++++++++++++++++++++++++++++++++++++++++++++ tests/as_ref.rs | 82 +++++++++++++++++++++- 2 files changed, 258 insertions(+), 1 deletion(-) diff --git a/tests/as_mut.rs b/tests/as_mut.rs index d3c6be8a..c48e9c81 100644 --- a/tests/as_mut.rs +++ b/tests/as_mut.rs @@ -99,6 +99,30 @@ mod single_field { assert!(ptr::eq(rf, item.0.as_mut())); } + #[derive(AsMut)] + #[as_mut(i32, Foo)] + struct TypesWithInner(Foo); + + // Asserts that the macro expansion doesn't generate a blanket `AsMut` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. + impl AsMut for TypesWithInner { + fn as_mut(&mut self) -> &mut bool { + self.0.as_mut() + } + } + + #[test] + fn types_with_inner() { + let mut item = TypesWithInner(Foo(1, 2.0, false)); + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.0.as_mut())); + + let rf: &mut Foo = item.as_mut(); + assert!(ptr::eq(rf, &mut item.0)); + } + #[derive(AsMut)] struct FieldTypes(#[as_mut(i32, f64)] Foo); @@ -131,6 +155,29 @@ mod single_field { assert!(ptr::eq(rf, item.0.as_mut())); } + #[derive(AsMut)] + struct FieldTypesWithInner(#[as_mut(i32, Foo)] Foo); + + // Asserts that the macro expansion doesn't generate a blanket `AsMut` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. + impl AsMut for FieldTypesWithInner { + fn as_mut(&mut self) -> &mut bool { + self.0.as_mut() + } + } + + #[test] + fn field_types_with_inner() { + let mut item = FieldTypesWithInner(Foo(1, 2.0, false)); + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.0.as_mut())); + + let rf: &mut Foo = item.as_mut(); + assert!(ptr::eq(rf, &mut item.0)); + } + mod generic { use super::*; @@ -201,6 +248,17 @@ mod single_field { assert!(ptr::eq(rf, item.0.as_mut())); } + #[derive(AsMut)] + #[as_mut(Vec)] + struct TypesInner(Vec); + + #[test] + fn types_inner() { + let mut item = TypesInner(vec![1i32]); + + assert!(ptr::eq(item.as_mut(), &mut item.0)); + } + #[derive(AsMut)] struct FieldTypes(#[as_mut(i32, f64)] T); @@ -223,6 +281,16 @@ mod single_field { let rf: &mut f64 = item.as_mut(); assert!(ptr::eq(rf, item.0.as_mut())); } + + #[derive(AsMut)] + struct FieldTypesInner(#[as_mut(Vec)] Vec); + + #[test] + fn field_types_inner() { + let mut item = FieldTypesInner(vec![1i32]); + + assert!(ptr::eq(item.as_mut(), &mut item.0)); + } } } @@ -327,6 +395,34 @@ mod single_field { assert!(ptr::eq(rf, item.first.as_mut())); } + #[derive(AsMut)] + #[as_mut(i32, Foo)] + struct TypesWithInner { + first: Foo, + } + + // Asserts that the macro expansion doesn't generate a blanket `AsMut` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. + impl AsMut for TypesWithInner { + fn as_mut(&mut self) -> &mut bool { + self.first.as_mut() + } + } + + #[test] + fn types_with_inner() { + let mut item = TypesWithInner { + first: Foo(1, 2.0, false), + }; + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.first.as_mut())); + + let rf: &mut Foo = item.as_mut(); + assert!(ptr::eq(rf, &mut item.first)); + } + #[derive(AsMut)] struct FieldTypes { #[as_mut(i32, f64)] @@ -364,6 +460,34 @@ mod single_field { assert!(ptr::eq(rf, item.first.as_mut())); } + #[derive(AsMut)] + struct FieldTypesWithInner { + #[as_mut(i32, Foo)] + first: Foo, + } + + // Asserts that the macro expansion doesn't generate a blanket `AsMut` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. + impl AsMut for FieldTypesWithInner { + fn as_mut(&mut self) -> &mut bool { + self.first.as_mut() + } + } + + #[test] + fn field_types_with_inner() { + let mut item = FieldTypesWithInner { + first: Foo(1, 2.0, false), + }; + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.first.as_mut())); + + let rf: &mut Foo = item.as_mut(); + assert!(ptr::eq(rf, &mut item.first)); + } + mod generic { use super::*; @@ -456,6 +580,19 @@ mod single_field { assert!(ptr::eq(rf, item.first.as_mut())); } + #[derive(AsMut)] + #[as_mut(Vec)] + struct TypesInner { + first: Vec, + } + + #[test] + fn types_inner() { + let mut item = TypesInner { first: vec![1i32] }; + + assert!(ptr::eq(item.as_mut(), &mut item.first)); + } + #[derive(AsMut)] struct FieldTypes { #[as_mut(i32, f64)] @@ -483,6 +620,19 @@ mod single_field { let rf: &mut f64 = item.as_mut(); assert!(ptr::eq(rf, item.first.as_mut())); } + + #[derive(AsMut)] + struct FieldTypesInner { + #[as_mut(Vec)] + first: Vec, + } + + #[test] + fn field_types_inner() { + let mut item = FieldTypesInner { first: vec![1i32] }; + + assert!(ptr::eq(item.as_mut(), &mut item.first)); + } } } } @@ -638,6 +788,16 @@ mod multi_field { let rf: &mut [u8] = item.as_mut(); assert!(ptr::eq(rf, item.1.as_mut())); } + + #[derive(AsMut)] + struct TypesInner(#[as_mut(Vec)] Vec, U); + + #[test] + fn types_inner() { + let mut item = TypesInner(vec![1i32], 2i32); + + assert!(ptr::eq(item.as_mut(), &mut item.0)); + } } } @@ -869,6 +1029,23 @@ mod multi_field { let rf: &mut [u8] = item.as_mut(); assert!(ptr::eq(rf, item.second.as_mut())); } + + #[derive(AsMut)] + struct TypesInner { + #[as_mut(Vec)] + first: Vec, + second: U, + } + + #[test] + fn types_inner() { + let mut item = TypesInner { + first: vec![1i32], + second: 2i32, + }; + + assert!(ptr::eq(item.as_mut(), &mut item.first)); + } } } } diff --git a/tests/as_ref.rs b/tests/as_ref.rs index 405bc688..160847cf 100644 --- a/tests/as_ref.rs +++ b/tests/as_ref.rs @@ -71,7 +71,7 @@ mod single_field { struct Types(Foo); // Asserts that the macro expansion doesn't generate a blanket `AsRef` - // impl forwarding to the field type, by producing atrait implementations + // impl forwarding to the field type, by producing a trait implementations // conflict error during compilation, if it does. impl AsRef for Types { fn as_ref(&self) -> &bool { @@ -250,6 +250,17 @@ mod single_field { assert!(ptr::eq(rf, item.0.as_ref())); } + #[derive(AsRef)] + #[as_ref(Vec)] + struct TypesInner(Vec); + + #[test] + fn types_inner() { + let item = TypesInner(vec![1i32]); + + assert!(ptr::eq(item.as_ref(), &item.0)); + } + #[derive(AsRef)] struct FieldTypes(#[as_ref(i32, f64)] T); @@ -272,6 +283,16 @@ mod single_field { let rf: &f64 = item.as_ref(); assert!(ptr::eq(rf, item.0.as_ref())); } + + #[derive(AsRef)] + struct FieldTypesInner(#[as_ref(Vec)] Vec); + + #[test] + fn field_types_inner() { + let item = FieldTypesInner(vec![1i32]); + + assert!(ptr::eq(item.as_ref(), &item.0)); + } } } @@ -563,6 +584,19 @@ mod single_field { assert!(ptr::eq(rf, item.first.as_ref())); } + #[derive(AsRef)] + #[as_ref(Vec)] + struct TypesInner { + first: Vec, + } + + #[test] + fn types_inner() { + let item = TypesInner { first: vec![1i32] }; + + assert!(ptr::eq(item.as_ref(), &item.first)); + } + #[derive(AsRef)] struct FieldTypes { #[as_ref(i32, f64)] @@ -590,6 +624,19 @@ mod single_field { let rf: &f64 = item.as_ref(); assert!(ptr::eq(rf, item.first.as_ref())); } + + #[derive(AsRef)] + struct FieldTypesInner { + #[as_ref(Vec)] + first: Vec, + } + + #[test] + fn field_types_inner() { + let item = FieldTypesInner { first: vec![1i32] }; + + assert!(ptr::eq(item.as_ref(), &item.first)); + } } } } @@ -678,6 +725,9 @@ mod multi_field { let rf: &str = item.as_ref(); assert!(ptr::eq(rf, item.0.as_ref())); + let rf: &String = item.as_ref(); + assert!(ptr::eq(rf, &item.0)); + let rf: &[u8] = item.as_ref(); assert!(ptr::eq(rf, item.1.as_ref())); } @@ -742,6 +792,16 @@ mod multi_field { let rf: &[u8] = item.as_ref(); assert!(ptr::eq(rf, item.1.as_ref())); } + + #[derive(AsRef)] + struct TypesInner(#[as_ref(Vec)] Vec, U); + + #[test] + fn types_inner() { + let item = TypesInner(vec![1i32], 2i32); + + assert!(ptr::eq(item.as_ref(), &item.0)); + } } } @@ -866,6 +926,9 @@ mod multi_field { let rf: &str = item.as_ref(); assert!(ptr::eq(rf, item.first.as_ref())); + let rf: &String = item.as_ref(); + assert!(ptr::eq(rf, &item.first)); + let rf: &[u8] = item.as_ref(); assert!(ptr::eq(rf, item.second.as_ref())); } @@ -970,6 +1033,23 @@ mod multi_field { let rf: &[u8] = item.as_ref(); assert!(ptr::eq(rf, item.second.as_ref())); } + + #[derive(AsRef)] + struct TypesInner { + #[as_ref(Vec)] + first: Vec, + second: U, + } + + #[test] + fn types_inner() { + let item = TypesInner { + first: vec![1i32], + second: 2i32, + }; + + assert!(ptr::eq(item.as_ref(), &item.first)); + } } } } From 787ca1e8eb6a93a935f4f2f80ce5e720eebcbcd3 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Wed, 30 Aug 2023 21:25:36 +0300 Subject: [PATCH 27/58] Update AsRef and AsMut docs --- impl/doc/as_mut.md | 36 +++++++++++++++++++++++------------- impl/doc/as_ref.md | 38 ++++++++++++++++++++++++-------------- 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/impl/doc/as_mut.md b/impl/doc/as_mut.md index 0b194d11..29516be8 100644 --- a/impl/doc/as_mut.md +++ b/impl/doc/as_mut.md @@ -61,34 +61,44 @@ where } ``` -It's also possible to specify concrete types to derive forwarded -impls for with `#[as_mut()]`. +It's also possible to specify concrete types to derive impls for with `#[as_mut()]`. + +These types can include both the type of the field, and types for which the field type implements `AsMut`. ```rust # use derive_more::AsMut; # #[derive(AsMut)] -#[as_mut(str, [u8])] +#[as_mut(str, [u8], String)] struct Types(String); let mut item = Types("test".to_owned()); let _: &mut str = item.as_mut(); let _: &mut [u8] = item.as_mut(); +let _: &mut String = item.as_mut();_ ``` -Generates: +When either the field type or the type specified to convert into contain type parameters, +they're compared for string equality, and when there's no match assumed to be different types. + +For example ```rust -# struct Types(String); -impl AsMut for Types { - fn as_mut(&mut self) -> &mut str { - >::as_mut(&mut self.0) - } -} +# use derive_more::AsMut; +# +#[derive(AsMut)] +#[as_ref(i32)] +struct Generic(T); +``` -impl AsMut<[u8]> for Types { - fn as_mut(&mut self) -> &mut [u8] { - >::as_mut(&mut self.0) +Generates a forwarded impl + +```rust +# struct Generic(T); +# +impl> AsMut for Generic { + fn as_mut(&self) -> &i32 { + >::as_mut(&mut self.0) } } ``` diff --git a/impl/doc/as_ref.md b/impl/doc/as_ref.md index edbe38a1..2170299d 100644 --- a/impl/doc/as_ref.md +++ b/impl/doc/as_ref.md @@ -61,34 +61,44 @@ where } ``` -It's also possible to specify concrete types to derive forwarded -impls for with `#[as_ref()]`. +It's also possible to specify concrete types to derive impls for with `#[as_ref()]`. + +These types can include both the type of the field, and types for which the field type implements `AsRef`. ```rust # use derive_more::AsRef; # #[derive(AsRef)] -#[as_ref(str, [u8])] +#[as_ref(str, [u8], String)] struct Types(String); let item = Types("test".to_owned()); let _: &str = item.as_ref(); let _: &[u8] = item.as_ref(); +let _: &String = item.as_ref(); ``` -Generates: +When either the field type or the type specified to convert into contain type parameters, +they're compared for string equality, and when there's no match assumed to be different types. + +For example ```rust -# struct Types(String); -impl AsRef for Types { - fn as_ref(&self) -> &str { - >::as_ref(&self.0) - } -} +# use derive_more::AsRef; +# +#[derive(AsRef)] +#[as_ref(i32)] +struct Generic(T); +``` -impl AsRef<[u8]> for Types { - fn as_ref(&self) -> &[u8] { - >::as_ref(&self.0) +Generates a forwarded impl + +```rust +# struct Generic(T); +# +impl> AsRef for Generic { + fn as_ref(&self) -> &i32 { + >::as_ref(&self.0) } } ``` @@ -123,7 +133,7 @@ Generates: # valid: bool, # } impl AsRef for MyWrapper { - fn as_ref(&self) -> &String { + fn as_ref(&self) -> &str { >::as_ref(&self.name) } } From 2c8537b34397942fe549b579d8c319786bc6ca1f Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Thu, 31 Aug 2023 11:49:15 +0300 Subject: [PATCH 28/58] Fix specialization machinery on no_std --- src/as.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/as.rs b/src/as.rs index e31711b9..e7907850 100644 --- a/src/as.rs +++ b/src/as.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -pub struct Conv(PhantomData<(Box, Box)>); +pub struct Conv(PhantomData<(*const Frm, *const To)>); impl Default for Conv { fn default() -> Self { From 40e1f74cca8fa3ab5d76267e68606779c4c8f022 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Thu, 31 Aug 2023 11:51:33 +0300 Subject: [PATCH 29/58] Fix clippy warning in parsing utils --- impl/src/parsing.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/impl/src/parsing.rs b/impl/src/parsing.rs index 42b66376..6e038cf4 100644 --- a/impl/src/parsing.rs +++ b/impl/src/parsing.rs @@ -243,15 +243,15 @@ pub fn seq( mut parsers: [&mut dyn FnMut(Cursor<'_>) -> ParsingResult<'_>; N], ) -> impl FnMut(Cursor<'_>) -> ParsingResult<'_> + '_ { move |c| { - parsers - .iter_mut() - .fold(Some((TokenStream::new(), c)), |out, parser| { - let (mut out, mut c) = out?; + parsers.iter_mut().try_fold( + (TokenStream::new(), c), + |(mut out, mut c), parser| { let (stream, cursor) = parser(c)?; out.extend(stream); c = cursor; Some((out, c)) - }) + }, + ) } } From 7897d9f37afedfc25a014396b231741dbe2ca9f2 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Thu, 31 Aug 2023 12:30:29 +0300 Subject: [PATCH 30/58] Rework expansion to make algorithm more clear --- impl/src/as/mod.rs | 133 ++++++++++++++++++++++++--------------------- 1 file changed, 70 insertions(+), 63 deletions(-) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index e22e32e4..6d7fde2b 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -184,21 +184,7 @@ impl<'a> ToTokens for Expansion<'a> { let (trait_ident, method_ident, mut_) = &self.trait_info; let ty_ident = &self.ident; - let (is_blanket, is_forward, return_tys) = match &self.args { - Some(AsArgs::Forward(_)) => ( - true, - true, - Either::Left(std::iter::once(Cow::Owned(parse_quote! { __AsT }))), - ), - Some(AsArgs::Types(tys)) => { - (false, true, Either::Right(tys.iter().map(Cow::Borrowed))) - } - None => ( - false, - false, - Either::Left(std::iter::once(Cow::Borrowed(field_ty))), - ), - }; + let field_ref = quote! { & #mut_ self.#field_ident }; let param_idents: HashSet<_> = self .generics @@ -208,73 +194,94 @@ impl<'a> ToTokens for Expansion<'a> { let field_contains_param = contains_any_of(&self.field.ty, ¶m_idents); - return_tys - .map(|return_ty| { - let contains_param = - field_contains_param || contains_any_of(&return_ty, ¶m_idents); + let is_blanket = matches!(&self.args, Some(AsArgs::Forward(_))); + + let return_tys = match &self.args { + Some(AsArgs::Forward(_)) => { + Either::Left(std::iter::once(Cow::Owned(parse_quote! { __AsT }))) + } + Some(AsArgs::Types(tys)) => Either::Right(tys.iter().map(Cow::Borrowed)), + None => Either::Left(std::iter::once(Cow::Borrowed(field_ty))), + }; + + for return_ty in return_tys { + let impl_version = 'ver: { + if is_blanket { + break 'ver ImplVersion::Forwarded; + } + + if field_ty == return_ty.as_ref() { + break 'ver ImplVersion::Direct; + } - let is_field_type = &self.field.ty == return_ty.as_ref(); + if field_contains_param || contains_any_of(&return_ty, ¶m_idents) { + break 'ver ImplVersion::Forwarded; + } - let is_forward = is_forward && !is_field_type; + ImplVersion::Specialized + }; - let trait_ty = quote! { ::core::convert::#trait_ident <#return_ty> }; + let trait_ty = quote! { ::core::convert::#trait_ident <#return_ty> }; - let generics = if is_forward { + let generics = match &impl_version { + ImplVersion::Forwarded => { let mut generics = self.generics.clone(); + + generics + .make_where_clause() + .predicates + .push(parse_quote! { #field_ty: #trait_ty }); + if is_blanket { - generics.params.push(parse_quote! { #return_ty }); generics - .make_where_clause() - .predicates + .params .push(parse_quote! { #return_ty: ?::core::marker::Sized }); } - if is_blanket || contains_param { - generics - .make_where_clause() - .predicates - .push(parse_quote! { #field_ty: #trait_ty }); - } Cow::Owned(generics) - } else { + } + ImplVersion::Direct | ImplVersion::Specialized => { Cow::Borrowed(self.generics) - }; - - let (impl_gens, _, where_clause) = generics.split_for_impl(); - let (_, ty_gens, _) = self.generics.split_for_impl(); - - let mut body = quote! { & #mut_ self.#field_ident }; - if is_forward { - if is_blanket || contains_param { - body = quote! { - <#field_ty as #trait_ty>::#method_ident(#body) - }; - } else { - body = quote! { - use ::derive_more::__private::ExtractRef as _; - - let conv: ::derive_more::__private::Conv<& #mut_ #field_ty, #return_ty> - = Default::default(); - (&&conv).__extract_ref(#body) - }; - } } - - quote! { - #[automatically_derived] - impl #impl_gens #trait_ty for #ty_ident #ty_gens #where_clause { - #[inline] - fn #method_ident(& #mut_ self) -> & #mut_ #return_ty { - #body - } + }; + + let (impl_gens, _, where_clause) = generics.split_for_impl(); + let (_, ty_gens, _) = self.generics.split_for_impl(); + + let body = match &impl_version { + ImplVersion::Direct => Cow::Borrowed(&field_ref), + ImplVersion::Forwarded => Cow::Owned(quote! { + <#field_ty as #trait_ty>::#method_ident(#field_ref) + }), + ImplVersion::Specialized => Cow::Owned(quote! { + use ::derive_more::__private::ExtractRef as _; + + let conv: ::derive_more::__private::Conv<& #mut_ #field_ty, #return_ty> + = Default::default(); + (&&conv).__extract_ref(#field_ref) + }), + }; + + quote! { + #[automatically_derived] + impl #impl_gens #trait_ty for #ty_ident #ty_gens #where_clause { + #[inline] + fn #method_ident(& #mut_ self) -> & #mut_ #return_ty { + #body } } - }) - .collect::() + } .to_tokens(tokens); + } } } +enum ImplVersion { + Direct, + Specialized, + Forwarded, +} + /// Representation of an [`AsRef`]/[`AsMut`] derive macro struct container attribute. /// /// ```rust,ignore From abcf391ba718e01f539c69df3b799065be653e6f Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Thu, 31 Aug 2023 12:35:40 +0300 Subject: [PATCH 31/58] Use universal function call syntax in the expansion --- impl/src/as/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index 6d7fde2b..4d3ae018 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -256,8 +256,9 @@ impl<'a> ToTokens for Expansion<'a> { ImplVersion::Specialized => Cow::Owned(quote! { use ::derive_more::__private::ExtractRef as _; - let conv: ::derive_more::__private::Conv<& #mut_ #field_ty, #return_ty> - = Default::default(); + let conv = + <::derive_more::__private::Conv<& #mut_ #field_ty, #return_ty> + as ::core::default::Default>::default(); (&&conv).__extract_ref(#field_ref) }), }; From 8ddb21c9c8c5ca59db62cdb98cdbafd5e2238981 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Thu, 31 Aug 2023 12:42:17 +0300 Subject: [PATCH 32/58] Fix trait order --- src/as.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/as.rs b/src/as.rs index e7907850..8997214f 100644 --- a/src/as.rs +++ b/src/as.rs @@ -42,7 +42,7 @@ impl<'a, T: ?Sized> ExtractRef for &Conv<&'a mut T, T> { } } -impl<'a, Frm: ?Sized + AsMut, To: ?Sized + 'a> ExtractRef +impl<'a, Frm: ?Sized + AsMut, To: 'a + ?Sized> ExtractRef for Conv<&'a mut Frm, To> { type Frm = &'a mut Frm; From ffe12cbf3e42f4849ab5889663845c24488bdec5 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Thu, 31 Aug 2023 13:04:03 +0300 Subject: [PATCH 33/58] Rename ImplVersion to ImplKind, add docs --- impl/src/as/mod.rs | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index 4d3ae018..b54275fa 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -205,26 +205,26 @@ impl<'a> ToTokens for Expansion<'a> { }; for return_ty in return_tys { - let impl_version = 'ver: { + let impl_kind = 'ver: { if is_blanket { - break 'ver ImplVersion::Forwarded; + break 'ver ImplKind::Forwarded; } if field_ty == return_ty.as_ref() { - break 'ver ImplVersion::Direct; + break 'ver ImplKind::Direct; } if field_contains_param || contains_any_of(&return_ty, ¶m_idents) { - break 'ver ImplVersion::Forwarded; + break 'ver ImplKind::Forwarded; } - ImplVersion::Specialized + ImplKind::Specialized }; let trait_ty = quote! { ::core::convert::#trait_ident <#return_ty> }; - let generics = match &impl_version { - ImplVersion::Forwarded => { + let generics = match &impl_kind { + ImplKind::Forwarded => { let mut generics = self.generics.clone(); generics @@ -240,7 +240,7 @@ impl<'a> ToTokens for Expansion<'a> { Cow::Owned(generics) } - ImplVersion::Direct | ImplVersion::Specialized => { + ImplKind::Direct | ImplKind::Specialized => { Cow::Borrowed(self.generics) } }; @@ -248,12 +248,12 @@ impl<'a> ToTokens for Expansion<'a> { let (impl_gens, _, where_clause) = generics.split_for_impl(); let (_, ty_gens, _) = self.generics.split_for_impl(); - let body = match &impl_version { - ImplVersion::Direct => Cow::Borrowed(&field_ref), - ImplVersion::Forwarded => Cow::Owned(quote! { + let body = match &impl_kind { + ImplKind::Direct => Cow::Borrowed(&field_ref), + ImplKind::Forwarded => Cow::Owned(quote! { <#field_ty as #trait_ty>::#method_ident(#field_ref) }), - ImplVersion::Specialized => Cow::Owned(quote! { + ImplKind::Specialized => Cow::Owned(quote! { use ::derive_more::__private::ExtractRef as _; let conv = @@ -277,10 +277,16 @@ impl<'a> ToTokens for Expansion<'a> { } } -enum ImplVersion { +/// The kind of impl generated, chosen based on attribute args. +enum ImplKind { + /// Returns a reference to a field. Direct, - Specialized, + /// Calls `as_ref`/`as_mut` on a field Forwarded, + /// Uses autoderef-based specialization to determine whether + /// to use direct or forwarded based on whether the field + /// and return type match. + Specialized, } /// Representation of an [`AsRef`]/[`AsMut`] derive macro struct container attribute. From 8a0bf5fdb76e8f16a9f54c2cc3b7d2ded05e8aab Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Thu, 31 Aug 2023 13:30:07 +0300 Subject: [PATCH 34/58] Fix doc comment --- impl/src/as/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index b54275fa..1aa0b6e2 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -386,7 +386,8 @@ enum AsArgs { /// Blanket impl, fully forwarding to the field type. Forward(forward::Attribute), - /// Forward implementation, but only impl for specified types. + /// Impl for specified types, which can include both the type of the field, + /// and types for which the field type implements `AsRef` Types(Punctuated), } From f027d5d4e540f9598fd76d3d237f425104657ba0 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Thu, 31 Aug 2023 13:47:56 +0300 Subject: [PATCH 35/58] Improve docs --- impl/doc/as_mut.md | 61 +++++++++++++++++++++++++++++++++++----------- impl/doc/as_ref.md | 50 ++++++++++++++++++++++++++++++------- 2 files changed, 88 insertions(+), 23 deletions(-) diff --git a/impl/doc/as_mut.md b/impl/doc/as_mut.md index 29516be8..09cd73cf 100644 --- a/impl/doc/as_mut.md +++ b/impl/doc/as_mut.md @@ -46,17 +46,17 @@ let mut item = SingleFieldForward(vec![]); let _: &mut [i32] = (&mut item).as_mut(); ``` -This generates: +This generates code equivalent to: ```rust # struct SingleFieldForward(Vec); -impl<__AsT: ?::core::marker::Sized> ::core::convert::AsMut<__AsT> for SingleFieldForward +impl AsMut for SingleFieldForward where - Vec: ::core::convert::AsMut<__AsT>, + Vec: AsMut, { #[inline] - fn as_mut(&mut self) -> &mut __AsT { - as ::core::convert::AsMut<__AsT>>::as_mut(&mut self.0) + fn as_mut(&mut self) -> &mut T { + self.0.as_mut() } } ``` @@ -87,7 +87,7 @@ For example # use derive_more::AsMut; # #[derive(AsMut)] -#[as_ref(i32)] +#[as_mut(i32)] struct Generic(T); ``` @@ -97,8 +97,42 @@ Generates a forwarded impl # struct Generic(T); # impl> AsMut for Generic { - fn as_mut(&self) -> &i32 { - >::as_mut(&mut self.0) + fn as_mut(&mut self) -> &mut i32 { + self.0.as_mut() + } +} +``` + +And + +```rust +# use derive_more::AsMut; +# +#[derive(AsMut)] +#[as_mut(T)] +struct Generic(T); +``` + +Generates + +```rust +# struct Generic(T); +# +impl AsMut for Generic { + fn as_mut(&mut self) -> &mut T { + &mut self.0 + } +} +``` + +Generating code like this is not supported + +```rust +# struct Generic(T); +# +impl AsMut for Generic { + fn as_mut(&mut self) -> &mut i32 { + &mut self.0 } } ``` @@ -133,7 +167,7 @@ Generates: # } impl AsMut for MyWrapper { fn as_mut(&mut self) -> &mut String { - >::as_mut(&mut self.name) + self.name.as_mut() } } @@ -148,11 +182,10 @@ Only conversions that use a single field are possible with this derive. Something like this wouldn't work, due to the nature of the `AsMut` trait: ```rust,compile_fail -# use derive_more::AsRef +# use derive_more::AsMut # -// Error! `#[as_ref(...)]` attribute can only be placed on structs with exactly one field -#[derive(AsRef)] -#[as_ref((str, [u8]))] +#[derive(AsMut)] +#[as_mut((str, [u8]))] struct MyWrapper(String, Vec) ``` @@ -230,7 +263,7 @@ struct ForwardWithOther { } ``` -Multiple forwarded impls with concrete types, however, can be used. +Multiple forwarded impls with different concrete types, however, can be used. ```rust # use derive_more::AsMut; diff --git a/impl/doc/as_ref.md b/impl/doc/as_ref.md index 2170299d..ad66ca6f 100644 --- a/impl/doc/as_ref.md +++ b/impl/doc/as_ref.md @@ -46,17 +46,17 @@ let item = SingleFieldForward(vec![]); let _: &[i32] = (&item).as_ref(); ``` -This generates: +This generates code equivalent to: ```rust # struct SingleFieldForward(Vec); -impl<__AsT: ?::core::marker::Sized> ::core::convert::AsRef<__AsT> for SingleFieldForward +impl AsRef for SingleFieldForward where - Vec: ::core::convert::AsRef<__AsT>, + Vec: AsRef, { #[inline] - fn as_ref(&self) -> &__AsT { - as ::core::convert::AsRef<__AsT>>::as_ref(&self.0) + fn as_ref(&self) -> &T { + self.0.as_ref() } } ``` @@ -98,11 +98,44 @@ Generates a forwarded impl # impl> AsRef for Generic { fn as_ref(&self) -> &i32 { - >::as_ref(&self.0) + self.0.as_ref() } } ``` +And + +```rust +# use derive_more::AsRef; +# +#[derive(AsRef)] +#[as_ref(T)] +struct Generic(T); +``` + +Generates + +```rust +# struct Generic(T); +# +impl AsRef for Generic { + fn as_ref(&self) -> &T { + &self.0 + } +} +``` + +Generating code like this is not supported + +```rust +# struct Generic(T); +# +impl AsRef for Generic { + fn as_ref(&self) -> &i32 { + &self.0 + } +} +``` ## Structs with Multiple Fields @@ -134,7 +167,7 @@ Generates: # } impl AsRef for MyWrapper { fn as_ref(&self) -> &str { - >::as_ref(&self.name) + self.name.as_ref() } } @@ -151,7 +184,6 @@ Something like this wouldn't work, due to the nature of the `AsRef` trait: ```rust,compile_fail # use derive_more::AsRef # -// Error! `#[as_ref(...)]` attribute can only be placed on structs with exactly one field #[derive(AsRef)] #[as_ref((str, [u8]))] struct MyWrapper(String, Vec) @@ -232,7 +264,7 @@ struct ForwardWithOther { } ``` -Multiple forwarded impls with concrete types, however, can be used. +Multiple forwarded impls with different concrete types, however, can be used. ```rust # use derive_more::AsRef; From 929b0ed7eaa696698468398069b0fdb582390497 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Thu, 31 Aug 2023 14:12:54 +0300 Subject: [PATCH 36/58] Rename helper type search function --- impl/src/as/mod.rs | 8 +++++--- impl/src/utils.rs | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index 1aa0b6e2..5c93d429 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -15,7 +15,7 @@ use syn::{ Token, }; -use crate::utils::{contains_any_of, forward, skip, Either, HashSet, Spanning}; +use crate::utils::{forward, skip, type_contains_any_of, Either, HashSet, Spanning}; /// Expands an [`AsRef`]/[`AsMut`] derive macro. pub fn expand( @@ -192,7 +192,7 @@ impl<'a> ToTokens for Expansion<'a> { .map(|param| ¶m.ident) .collect(); - let field_contains_param = contains_any_of(&self.field.ty, ¶m_idents); + let field_contains_param = type_contains_any_of(field_ty, ¶m_idents); let is_blanket = matches!(&self.args, Some(AsArgs::Forward(_))); @@ -214,7 +214,9 @@ impl<'a> ToTokens for Expansion<'a> { break 'ver ImplKind::Direct; } - if field_contains_param || contains_any_of(&return_ty, ¶m_idents) { + if field_contains_param + || type_contains_any_of(&return_ty, ¶m_idents) + { break 'ver ImplKind::Forwarded; } diff --git a/impl/src/utils.rs b/impl/src/utils.rs index a70c8d1a..6ce2eb1d 100644 --- a/impl/src/utils.rs +++ b/impl/src/utils.rs @@ -32,7 +32,7 @@ pub(crate) use self::fields_ext::FieldsExt; pub(crate) use self::spanning::Spanning; #[cfg(feature = "as_ref")] -pub(crate) use self::type_search::contains_any_of; +pub(crate) use self::type_search::type_contains_any_of; #[derive(Clone, Copy, Default)] pub struct DeterministicState; @@ -1712,7 +1712,7 @@ mod type_search { } } - pub(crate) fn contains_any_of( + pub(crate) fn type_contains_any_of( ty: &syn::Type, search: &HashSet<&syn::Ident>, ) -> bool { From cf2e1003a57619785620cb1ece743326b4caed4f Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Thu, 31 Aug 2023 14:18:58 +0300 Subject: [PATCH 37/58] Move ImplKind definition inside to_tokens --- impl/src/as/mod.rs | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index 5c93d429..3df0e457 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -205,28 +205,42 @@ impl<'a> ToTokens for Expansion<'a> { }; for return_ty in return_tys { + use ImplKind::*; + + /// The kind of impl generated, chosen based on attribute args. + enum ImplKind { + /// Returns a reference to a field. + Direct, + /// Calls `as_ref`/`as_mut` on a field + Forwarded, + /// Uses autoderef-based specialization to determine whether + /// to use direct or forwarded based on whether the field + /// and return type match. + Specialized, + } + let impl_kind = 'ver: { if is_blanket { - break 'ver ImplKind::Forwarded; + break 'ver Forwarded; } if field_ty == return_ty.as_ref() { - break 'ver ImplKind::Direct; + break 'ver Direct; } if field_contains_param || type_contains_any_of(&return_ty, ¶m_idents) { - break 'ver ImplKind::Forwarded; + break 'ver Forwarded; } - ImplKind::Specialized + Specialized }; let trait_ty = quote! { ::core::convert::#trait_ident <#return_ty> }; let generics = match &impl_kind { - ImplKind::Forwarded => { + Forwarded => { let mut generics = self.generics.clone(); generics @@ -242,20 +256,18 @@ impl<'a> ToTokens for Expansion<'a> { Cow::Owned(generics) } - ImplKind::Direct | ImplKind::Specialized => { - Cow::Borrowed(self.generics) - } + Direct | Specialized => Cow::Borrowed(self.generics), }; let (impl_gens, _, where_clause) = generics.split_for_impl(); let (_, ty_gens, _) = self.generics.split_for_impl(); let body = match &impl_kind { - ImplKind::Direct => Cow::Borrowed(&field_ref), - ImplKind::Forwarded => Cow::Owned(quote! { + Direct => Cow::Borrowed(&field_ref), + Forwarded => Cow::Owned(quote! { <#field_ty as #trait_ty>::#method_ident(#field_ref) }), - ImplKind::Specialized => Cow::Owned(quote! { + Specialized => Cow::Owned(quote! { use ::derive_more::__private::ExtractRef as _; let conv = @@ -279,18 +291,6 @@ impl<'a> ToTokens for Expansion<'a> { } } -/// The kind of impl generated, chosen based on attribute args. -enum ImplKind { - /// Returns a reference to a field. - Direct, - /// Calls `as_ref`/`as_mut` on a field - Forwarded, - /// Uses autoderef-based specialization to determine whether - /// to use direct or forwarded based on whether the field - /// and return type match. - Specialized, -} - /// Representation of an [`AsRef`]/[`AsMut`] derive macro struct container attribute. /// /// ```rust,ignore From 4adb99228e899e8e8c6f9b231efff6ae616a79b4 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Thu, 31 Aug 2023 14:32:53 +0300 Subject: [PATCH 38/58] Extract merging functions from parse_attrs --- impl/src/as/mod.rs | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index 3df0e457..b1d1c75f 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -353,12 +353,10 @@ impl FieldAttribute { if let Some(prev) = attrs { let span = prev.span.join(parsed.span).unwrap_or(prev.span); - match (prev.item, parsed.item) { - (Self::Args(AsArgs::Types(mut tys)), Self::Args(AsArgs::Types(more))) => { - tys.extend(more); - Ok(Some(Spanning::new(Self::Args(AsArgs::Types(tys)), span))) - } - _ => Err(syn::Error::new( + if let Some(new) = prev.item.merge(parsed.item) { + Ok(Some(Spanning::new(new, span))) + } else { + Err(syn::Error::new( parsed.span, format!("only single `#[{attr_ident}(...)]` attribute is allowed here") )) @@ -376,6 +374,15 @@ impl FieldAttribute { Self::Empty | Self::Skip(_) => None, } } + + /// Merges two [`FieldAttribute`]s, if possible + fn merge(self, other: Self) -> Option { + if let (Self::Args(args), Self::Args(more)) = (self, other) { + args.merge(more).map(Self::Args) + } else { + None + } + } } /// Arguments specifying which conversions should be generated. @@ -393,6 +400,18 @@ enum AsArgs { Types(Punctuated), } +impl AsArgs { + /// Merges two [`AsArgs`], if possible + fn merge(self, other: Self) -> Option { + if let (Self::Types(mut tys), Self::Types(more)) = (self, other) { + tys.extend(more); + Some(Self::Types(tys)) + } else { + None + } + } +} + impl Parse for AsArgs { fn parse(input: ParseStream) -> syn::Result { let ahead = input.fork(); @@ -421,12 +440,10 @@ impl StructAttribute { if let Some(prev) = attrs { let span = prev.span.join(parsed.span).unwrap_or(prev.span); - match (prev.item, parsed.item) { - (Self::Types(mut tys), Self::Types(more)) => { - tys.extend(more); - Ok(Some(Spanning::new(Self::Types(tys), span))) - }, - _ => Err(syn::Error::new( + if let Some(new) = prev.item.merge(parsed.item) { + Ok(Some(Spanning::new(new, span))) + } else { + Err(syn::Error::new( parsed.span, format!("only single `#[{attr_ident}(...)]` attribute is allowed here"), )) From 54b442b9a80417fe9000c2c45a10d2a968f4ea41 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Thu, 31 Aug 2023 15:59:36 +0300 Subject: [PATCH 39/58] Extend tests --- tests/as_mut.rs | 155 +++++++++++++++++++++++++++++++++++++++++++---- tests/as_ref.rs | 157 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 287 insertions(+), 25 deletions(-) diff --git a/tests/as_mut.rs b/tests/as_mut.rs index c48e9c81..72a65fbb 100644 --- a/tests/as_mut.rs +++ b/tests/as_mut.rs @@ -123,6 +123,33 @@ mod single_field { assert!(ptr::eq(rf, &mut item.0)); } + type RenamedFoo = Foo; + + #[derive(AsMut)] + #[as_mut(i32, RenamedFoo)] + struct TypesWithRenamedInner(Foo); + + // Asserts that the macro expansion doesn't generate a blanket `AsMut` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. + + impl AsMut for TypesWithRenamedInner { + fn as_mut(&mut self) -> &mut bool { + self.0.as_mut() + } + } + + #[test] + fn types_with_renamed_inner() { + let mut item = TypesWithRenamedInner(Foo(1, 2.0, false)); + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.0.as_mut())); + + let rf: &mut Foo = item.as_mut(); + assert!(ptr::eq(rf, &mut item.0)); + } + #[derive(AsMut)] struct FieldTypes(#[as_mut(i32, f64)] Foo); @@ -178,6 +205,29 @@ mod single_field { assert!(ptr::eq(rf, &mut item.0)); } + #[derive(AsMut)] + struct FieldTypesWithRenamedInner(#[as_mut(i32, RenamedFoo)] Foo); + + // Asserts that the macro expansion doesn't generate a blanket `AsMut` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. + impl AsMut for FieldTypesWithRenamedInner { + fn as_mut(&mut self) -> &mut bool { + self.0.as_mut() + } + } + + #[test] + fn field_types_with_renamed_inner() { + let mut item = FieldTypesWithRenamedInner(Foo(1, 2.0, false)); + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.0.as_mut())); + + let rf: &mut Foo = item.as_mut(); + assert!(ptr::eq(rf, &mut item.0)); + } + mod generic { use super::*; @@ -423,6 +473,36 @@ mod single_field { assert!(ptr::eq(rf, &mut item.first)); } + type RenamedFoo = Foo; + + #[derive(AsMut)] + #[as_mut(i32, RenamedFoo)] + struct TypesWithRenamedInner { + first: Foo, + } + + // Asserts that the macro expansion doesn't generate a blanket `AsMut` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. + impl AsMut for TypesWithRenamedInner { + fn as_mut(&mut self) -> &mut bool { + self.first.as_mut() + } + } + + #[test] + fn types_with_renamed_inner() { + let mut item = TypesWithRenamedInner { + first: Foo(1, 2.0, false), + }; + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.first.as_mut())); + + let rf: &mut Foo = item.as_mut(); + assert!(ptr::eq(rf, &mut item.first)); + } + #[derive(AsMut)] struct FieldTypes { #[as_mut(i32, f64)] @@ -488,6 +568,34 @@ mod single_field { assert!(ptr::eq(rf, &mut item.first)); } + #[derive(AsMut)] + struct FieldTypesWithRenamedInner { + #[as_mut(i32, RenamedFoo)] + first: Foo, + } + + // Asserts that the macro expansion doesn't generate a blanket `AsMut` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. + impl AsMut for FieldTypesWithRenamedInner { + fn as_mut(&mut self) -> &mut bool { + self.first.as_mut() + } + } + + #[test] + fn field_types_with_renamed_inner() { + let mut item = FieldTypesWithRenamedInner { + first: Foo(1, 2.0, false), + }; + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.first.as_mut())); + + let rf: &mut Foo = item.as_mut(); + assert!(ptr::eq(rf, &mut item.first)); + } + mod generic { use super::*; @@ -703,8 +811,13 @@ mod multi_field { assert!(ptr::eq(rf, item.0.as_mut())); } + type RenamedString = String; + #[derive(AsMut)] - struct Types(#[as_mut(str, String)] String, #[as_mut([u8])] Vec); + struct Types( + #[as_mut(str, RenamedString)] String, + #[as_mut([u8])] Vec, + ); // Asserts that the macro expansion doesn't generate `AsMut` impl for the field type, by // producing trait implementations conflict error during compilation, if it does. @@ -790,13 +903,23 @@ mod multi_field { } #[derive(AsMut)] - struct TypesInner(#[as_mut(Vec)] Vec, U); + struct TypesWithInner( + #[as_mut(Vec, [T])] Vec, + #[as_mut(str)] U, + ); #[test] - fn types_inner() { - let mut item = TypesInner(vec![1i32], 2i32); + fn types_with_inner() { + let mut item = TypesWithInner(vec![1i32], "a".to_owned()); - assert!(ptr::eq(item.as_mut(), &mut item.0)); + let rf: &mut Vec = item.as_mut(); + assert!(ptr::eq(rf, &mut item.0)); + + let rf: &mut [i32] = item.as_mut(); + assert!(ptr::eq(rf, item.0.as_mut())); + + let rf: &mut str = item.as_mut(); + assert!(ptr::eq(rf, item.1.as_mut())); } } } @@ -896,9 +1019,11 @@ mod multi_field { assert!(ptr::eq(rf, item.first.as_mut())); } + type RenamedString = String; + #[derive(AsMut)] struct Types { - #[as_mut(str, String)] + #[as_mut(str, RenamedString)] first: String, #[as_mut([u8])] second: Vec, @@ -1031,20 +1156,28 @@ mod multi_field { } #[derive(AsMut)] - struct TypesInner { - #[as_mut(Vec)] + struct TypesWithInner { + #[as_mut(Vec, [T])] first: Vec, + #[as_mut(str)] second: U, } #[test] fn types_inner() { - let mut item = TypesInner { + let mut item = TypesWithInner { first: vec![1i32], - second: 2i32, + second: "a".to_owned(), }; - assert!(ptr::eq(item.as_mut(), &mut item.first)); + let rf: &mut Vec = item.as_mut(); + assert!(ptr::eq(rf, &mut item.first)); + + let rf: &mut [i32] = item.as_mut(); + assert!(ptr::eq(rf, item.first.as_mut())); + + let rf: &mut str = item.as_mut(); + assert!(ptr::eq(rf, item.second.as_mut())); } } } diff --git a/tests/as_ref.rs b/tests/as_ref.rs index 160847cf..cf194c82 100644 --- a/tests/as_ref.rs +++ b/tests/as_ref.rs @@ -123,6 +123,33 @@ mod single_field { assert!(ptr::eq(rf, &item.0)); } + type RenamedFoo = Foo; + + #[derive(AsRef)] + #[as_ref(i32, RenamedFoo)] + struct TypesWithRenamedInner(Foo); + + // Asserts that the macro expansion doesn't generate a blanket `AsRef` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. + + impl AsRef for TypesWithRenamedInner { + fn as_ref(&self) -> &bool { + self.0.as_ref() + } + } + + #[test] + fn types_with_renamed_inner() { + let item = TypesWithRenamedInner(Foo(1, 2.0, false)); + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.0.as_ref())); + + let rf: &Foo = item.as_ref(); + assert!(ptr::eq(rf, &item.0)); + } + #[derive(AsRef)] struct FieldTypes(#[as_ref(i32, f64)] Foo); @@ -155,7 +182,28 @@ mod single_field { assert!(ptr::eq(rf, item.0.as_ref())); } - type RenamedFoo = Foo; + #[derive(AsRef)] + struct FieldTypesWithInner(#[as_ref(i32, Foo)] Foo); + + // Asserts that the macro expansion doesn't generate a blanket `AsRef` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. + impl AsRef for FieldTypesWithInner { + fn as_ref(&self) -> &bool { + self.0.as_ref() + } + } + + #[test] + fn field_types_with_inner() { + let item = FieldTypesWithInner(Foo(1, 2.0, false)); + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.0.as_ref())); + + let rf: &Foo = item.as_ref(); + assert!(ptr::eq(rf, &item.0)); + } #[derive(AsRef)] struct FieldTypesWithRenamedInner(#[as_ref(i32, RenamedFoo)] Foo); @@ -425,6 +473,36 @@ mod single_field { assert!(ptr::eq(rf, &item.first)); } + type RenamedFoo = Foo; + + #[derive(AsRef)] + #[as_ref(i32, RenamedFoo)] + struct TypesWithRenamedInner { + first: Foo, + } + + // Asserts that the macro expansion doesn't generate a blanket `AsRef` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. + impl AsRef for TypesWithRenamedInner { + fn as_ref(&self) -> &bool { + self.first.as_ref() + } + } + + #[test] + fn types_with_renamed_inner() { + let item = TypesWithRenamedInner { + first: Foo(1, 2.0, false), + }; + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.first.as_ref())); + + let rf: &Foo = item.as_ref(); + assert!(ptr::eq(rf, &item.first)); + } + #[derive(AsRef)] struct FieldTypes { #[as_ref(i32, f64)] @@ -462,7 +540,33 @@ mod single_field { assert!(ptr::eq(rf, item.first.as_ref())); } - type RenamedFoo = Foo; + #[derive(AsRef)] + struct FieldTypesWithInner { + #[as_ref(i32, Foo)] + first: Foo, + } + + // Asserts that the macro expansion doesn't generate a blanket `AsRef` + // impl forwarding to the field type, by producing a trait implementations + // conflict error during compilation, if it does. + impl AsRef for FieldTypesWithInner { + fn as_ref(&self) -> &bool { + self.first.as_ref() + } + } + + #[test] + fn field_types_with_inner() { + let item = FieldTypesWithInner { + first: Foo(1, 2.0, false), + }; + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.first.as_ref())); + + let rf: &Foo = item.as_ref(); + assert!(ptr::eq(rf, &item.first)); + } #[derive(AsRef)] struct FieldTypesWithRenamedInner { @@ -707,8 +811,13 @@ mod multi_field { assert!(ptr::eq(rf, item.0.as_ref())); } + type RenamedString = String; + #[derive(AsRef)] - struct Types(#[as_ref(str, String)] String, #[as_ref([u8])] Vec); + struct Types( + #[as_ref(str, RenamedString)] String, + #[as_ref([u8])] Vec, + ); // Asserts that the macro expansion doesn't generate `AsRef` impl for the field type, by // producing trait implementations conflict error during compilation, if it does. @@ -794,13 +903,23 @@ mod multi_field { } #[derive(AsRef)] - struct TypesInner(#[as_ref(Vec)] Vec, U); + struct TypesWithInner( + #[as_ref(Vec, [T])] Vec, + #[as_ref(str)] U, + ); #[test] - fn types_inner() { - let item = TypesInner(vec![1i32], 2i32); + fn types_with_inner() { + let item = TypesWithInner(vec![1i32], "a".to_owned()); - assert!(ptr::eq(item.as_ref(), &item.0)); + let rf: &Vec = item.as_ref(); + assert!(ptr::eq(rf, &item.0)); + + let rf: &[i32] = item.as_ref(); + assert!(ptr::eq(rf, item.0.as_ref())); + + let rf: &str = item.as_ref(); + assert!(ptr::eq(rf, item.1.as_ref())); } } } @@ -900,9 +1019,11 @@ mod multi_field { assert!(ptr::eq(rf, item.first.as_ref())); } + type RenamedString = String; + #[derive(AsRef)] struct Types { - #[as_ref(str, String)] + #[as_ref(str, RenamedString)] first: String, #[as_ref([u8])] second: Vec, @@ -1035,20 +1156,28 @@ mod multi_field { } #[derive(AsRef)] - struct TypesInner { - #[as_ref(Vec)] + struct TypesWithInner { + #[as_ref(Vec, [T])] first: Vec, + #[as_ref(str)] second: U, } #[test] - fn types_inner() { - let item = TypesInner { + fn types_with_inner() { + let item = TypesWithInner { first: vec![1i32], - second: 2i32, + second: "a".to_owned(), }; - assert!(ptr::eq(item.as_ref(), &item.first)); + let rf: &Vec = item.as_ref(); + assert!(ptr::eq(rf, &item.first)); + + let rf: &[i32] = item.as_ref(); + assert!(ptr::eq(rf, item.first.as_ref())); + + let rf: &str = item.as_ref(); + assert!(ptr::eq(rf, item.second.as_ref())); } } } From 7d3567a8dd2cd006fb52f54a46a7f71d4fb25d93 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Thu, 31 Aug 2023 16:06:06 +0300 Subject: [PATCH 40/58] Add compile_fail tests for renamed generic case --- tests/compile_fail/as_mut/renamed_generic.rs | 12 ++++++++++ .../as_mut/renamed_generic.stderr | 24 +++++++++++++++++++ tests/compile_fail/as_ref/renamed_generic.rs | 12 ++++++++++ .../as_ref/renamed_generic.stderr | 24 +++++++++++++++++++ 4 files changed, 72 insertions(+) create mode 100644 tests/compile_fail/as_mut/renamed_generic.rs create mode 100644 tests/compile_fail/as_mut/renamed_generic.stderr create mode 100644 tests/compile_fail/as_ref/renamed_generic.rs create mode 100644 tests/compile_fail/as_ref/renamed_generic.stderr diff --git a/tests/compile_fail/as_mut/renamed_generic.rs b/tests/compile_fail/as_mut/renamed_generic.rs new file mode 100644 index 00000000..0cdd82b9 --- /dev/null +++ b/tests/compile_fail/as_mut/renamed_generic.rs @@ -0,0 +1,12 @@ +struct Foo(T); + +type Bar = Foo; + +#[derive(derive_more::AsMut)] +#[as_mut(Bar)] +struct Baz(Foo); + +fn main() { + let mut item = Baz(Foo(1i32)); + let _: &mut Bar = item.as_mut(); +} diff --git a/tests/compile_fail/as_mut/renamed_generic.stderr b/tests/compile_fail/as_mut/renamed_generic.stderr new file mode 100644 index 00000000..1fe7d7af --- /dev/null +++ b/tests/compile_fail/as_mut/renamed_generic.stderr @@ -0,0 +1,24 @@ +error[E0599]: the method `as_mut` exists for struct `Baz`, but its trait bounds were not satisfied + --> tests/compile_fail/as_mut/renamed_generic.rs:11:33 + | +1 | struct Foo(T); + | ------------- doesn't satisfy `Foo: AsMut>` +... +7 | struct Baz(Foo); + | ------------- + | | + | method `as_mut` not found for this struct + | doesn't satisfy `Baz: AsMut>` +... +11 | let _: &mut Bar = item.as_mut(); + | ^^^^^^ method cannot be called on `Baz` due to unsatisfied trait bounds + | + = note: trait bound `Foo: AsMut>` was not satisfied +note: the trait `AsMut` must be implemented + --> $RUST/core/src/convert/mod.rs + | + | pub trait AsMut { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `as_mut`, perhaps you need to implement it: + candidate #1: `AsMut` diff --git a/tests/compile_fail/as_ref/renamed_generic.rs b/tests/compile_fail/as_ref/renamed_generic.rs new file mode 100644 index 00000000..bf766eb8 --- /dev/null +++ b/tests/compile_fail/as_ref/renamed_generic.rs @@ -0,0 +1,12 @@ +struct Foo(T); + +type Bar = Foo; + +#[derive(derive_more::AsRef)] +#[as_ref(Bar)] +struct Baz(Foo); + +fn main() { + let item = Baz(Foo(1i32)); + let _: &Bar = item.as_ref(); +} diff --git a/tests/compile_fail/as_ref/renamed_generic.stderr b/tests/compile_fail/as_ref/renamed_generic.stderr new file mode 100644 index 00000000..5c862edd --- /dev/null +++ b/tests/compile_fail/as_ref/renamed_generic.stderr @@ -0,0 +1,24 @@ +error[E0599]: the method `as_ref` exists for struct `Baz`, but its trait bounds were not satisfied + --> tests/compile_fail/as_ref/renamed_generic.rs:11:29 + | +1 | struct Foo(T); + | ------------- doesn't satisfy `Foo: AsRef>` +... +7 | struct Baz(Foo); + | ------------- + | | + | method `as_ref` not found for this struct + | doesn't satisfy `Baz: AsRef>` +... +11 | let _: &Bar = item.as_ref(); + | ^^^^^^ method cannot be called on `Baz` due to unsatisfied trait bounds + | + = note: trait bound `Foo: AsRef>` was not satisfied +note: the trait `AsRef` must be implemented + --> $RUST/core/src/convert/mod.rs + | + | pub trait AsRef { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `as_ref`, perhaps you need to implement it: + candidate #1: `AsRef` From ee7a484119a27a9ee9efe305f02eea159bc6d64e Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Thu, 31 Aug 2023 16:45:03 +0300 Subject: [PATCH 41/58] Fix compile_fail stderr --- tests/compile_fail/as_mut/renamed_generic.stderr | 3 --- tests/compile_fail/as_ref/renamed_generic.stderr | 3 --- 2 files changed, 6 deletions(-) diff --git a/tests/compile_fail/as_mut/renamed_generic.stderr b/tests/compile_fail/as_mut/renamed_generic.stderr index 1fe7d7af..f0838843 100644 --- a/tests/compile_fail/as_mut/renamed_generic.stderr +++ b/tests/compile_fail/as_mut/renamed_generic.stderr @@ -16,9 +16,6 @@ error[E0599]: the method `as_mut` exists for struct `Baz`, but its trait bo = note: trait bound `Foo: AsMut>` was not satisfied note: the trait `AsMut` must be implemented --> $RUST/core/src/convert/mod.rs - | - | pub trait AsMut { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `as_mut`, perhaps you need to implement it: candidate #1: `AsMut` diff --git a/tests/compile_fail/as_ref/renamed_generic.stderr b/tests/compile_fail/as_ref/renamed_generic.stderr index 5c862edd..22fd78a5 100644 --- a/tests/compile_fail/as_ref/renamed_generic.stderr +++ b/tests/compile_fail/as_ref/renamed_generic.stderr @@ -16,9 +16,6 @@ error[E0599]: the method `as_ref` exists for struct `Baz`, but its trait bo = note: trait bound `Foo: AsRef>` was not satisfied note: the trait `AsRef` must be implemented --> $RUST/core/src/convert/mod.rs - | - | pub trait AsRef { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `as_ref`, perhaps you need to implement it: candidate #1: `AsRef` From abbe52a3823f1098c3ecc19576b1554c382bb2e5 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Thu, 31 Aug 2023 17:29:09 +0300 Subject: [PATCH 42/58] Add non-generic field tests --- tests/as_mut.rs | 27 +++++++++++++++++++++++++++ tests/as_ref.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/tests/as_mut.rs b/tests/as_mut.rs index 72a65fbb..4bd72190 100644 --- a/tests/as_mut.rs +++ b/tests/as_mut.rs @@ -921,6 +921,16 @@ mod multi_field { let rf: &mut str = item.as_mut(); assert!(ptr::eq(rf, item.1.as_mut())); } + + #[derive(AsMut)] + struct FieldNonGeneric(#[as_mut([T])] Vec, T); + + #[test] + fn field_non_generic() { + let mut item = FieldNonGeneric(vec![], 2i32); + + assert!(ptr::eq(item.as_mut(), item.0.as_mut())); + } } } @@ -1179,6 +1189,23 @@ mod multi_field { let rf: &mut str = item.as_mut(); assert!(ptr::eq(rf, item.second.as_mut())); } + + #[derive(AsMut)] + struct FieldNonGeneric { + #[as_mut([T])] + first: Vec, + second: T, + } + + #[test] + fn field_non_generic() { + let mut item = FieldNonGeneric { + first: vec![], + second: 2i32, + }; + + assert!(ptr::eq(item.as_mut(), item.first.as_mut())); + } } } } diff --git a/tests/as_ref.rs b/tests/as_ref.rs index cf194c82..ebc51535 100644 --- a/tests/as_ref.rs +++ b/tests/as_ref.rs @@ -921,6 +921,16 @@ mod multi_field { let rf: &str = item.as_ref(); assert!(ptr::eq(rf, item.1.as_ref())); } + + #[derive(AsRef)] + struct FieldNonGeneric(#[as_ref([T])] Vec, T); + + #[test] + fn field_non_generic() { + let item = FieldNonGeneric(vec![], 2i32); + + assert!(ptr::eq(item.as_ref(), item.0.as_ref())); + } } } @@ -1179,6 +1189,23 @@ mod multi_field { let rf: &str = item.as_ref(); assert!(ptr::eq(rf, item.second.as_ref())); } + + #[derive(AsRef)] + struct FieldNonGeneric { + #[as_ref([T])] + first: Vec, + second: T, + } + + #[test] + fn field_non_generic() { + let item = FieldNonGeneric { + first: vec![], + second: 2i32, + }; + + assert!(ptr::eq(item.as_ref(), item.first.as_ref())); + } } } } From b90b268e019453e571b650424aa894cfb0ad1a60 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Thu, 31 Aug 2023 17:34:31 +0300 Subject: [PATCH 43/58] Use non-macro impls for helper struct --- tests/as_mut.rs | 19 ++++++++++++++++++- tests/as_ref.rs | 19 ++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/tests/as_mut.rs b/tests/as_mut.rs index 4bd72190..c74aff74 100644 --- a/tests/as_mut.rs +++ b/tests/as_mut.rs @@ -14,9 +14,26 @@ use core::ptr; use derive_more::AsMut; -#[derive(AsMut)] struct Foo(i32, f64, bool); +impl AsMut for Foo { + fn as_mut(&mut self) -> &mut i32 { + &mut self.0 + } +} + +impl AsMut for Foo { + fn as_mut(&mut self) -> &mut f64 { + &mut self.1 + } +} + +impl AsMut for Foo { + fn as_mut(&mut self) -> &mut bool { + &mut self.2 + } +} + mod single_field { use super::*; diff --git a/tests/as_ref.rs b/tests/as_ref.rs index ebc51535..8307a612 100644 --- a/tests/as_ref.rs +++ b/tests/as_ref.rs @@ -14,9 +14,26 @@ use core::ptr; use derive_more::AsRef; -#[derive(AsRef)] struct Foo(i32, f64, bool); +impl AsRef for Foo { + fn as_ref(&self) -> &i32 { + &self.0 + } +} + +impl AsRef for Foo { + fn as_ref(&self) -> &f64 { + &self.1 + } +} + +impl AsRef for Foo { + fn as_ref(&self) -> &bool { + &self.2 + } +} + mod single_field { use super::*; From 7642a66f93b970f00bede416bc745eaec7587ef3 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Thu, 31 Aug 2023 17:49:27 +0300 Subject: [PATCH 44/58] Add lifetime param tests --- tests/as_mut.rs | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ tests/as_ref.rs | 55 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/tests/as_mut.rs b/tests/as_mut.rs index c74aff74..4bbc037d 100644 --- a/tests/as_mut.rs +++ b/tests/as_mut.rs @@ -34,6 +34,21 @@ impl AsMut for Foo { } } +struct Bar(T); + +impl AsMut for Bar<&'static mut i32> { + fn as_mut(&mut self) -> &mut i32 { + self.0 + } +} + +impl Default for Bar<&'static mut i32> { + fn default() -> Self { + static mut STATIC: i32 = 0; + Self(unsafe { &mut STATIC }) + } +} + mod single_field { use super::*; @@ -358,6 +373,27 @@ mod single_field { assert!(ptr::eq(item.as_mut(), &mut item.0)); } + + #[derive(AsMut)] + #[as_mut(i32)] + struct Lifetime<'a>(Bar<&'a mut i32>); + + #[test] + fn lifetime() { + let mut item = Lifetime(Bar::default()); + + assert!(ptr::eq(item.as_mut(), item.0.as_mut())); + } + + #[derive(AsMut)] + struct FieldLifetime<'a>(#[as_mut(i32)] Bar<&'a mut i32>); + + #[test] + fn field_lifetime() { + let mut item = FieldLifetime(Bar::default()); + + assert!(ptr::eq(item.as_mut(), item.0.as_mut())); + } } } @@ -758,6 +794,36 @@ mod single_field { assert!(ptr::eq(item.as_mut(), &mut item.first)); } + + #[derive(AsMut)] + #[as_mut(i32)] + struct Lifetime<'a> { + first: Bar<&'a mut i32>, + } + + #[test] + fn lifetime() { + let mut item = Lifetime { + first: Bar::default(), + }; + + assert!(ptr::eq(item.as_mut(), item.first.as_mut())); + } + + #[derive(AsMut)] + struct FieldLifetime<'a> { + #[as_mut(i32)] + first: Bar<&'a mut i32>, + } + + #[test] + fn field_lifetime() { + let mut item = FieldLifetime { + first: Bar::default(), + }; + + assert!(ptr::eq(item.as_mut(), item.first.as_mut())); + } } } } diff --git a/tests/as_ref.rs b/tests/as_ref.rs index 8307a612..184d4485 100644 --- a/tests/as_ref.rs +++ b/tests/as_ref.rs @@ -34,6 +34,14 @@ impl AsRef for Foo { } } +struct Bar(T); + +impl AsRef for Bar<&'static i32> { + fn as_ref(&self) -> &i32 { + self.0 + } +} + mod single_field { use super::*; @@ -358,6 +366,27 @@ mod single_field { assert!(ptr::eq(item.as_ref(), &item.0)); } + + #[derive(AsRef)] + #[as_ref(i32)] + struct Lifetime<'a>(Bar<&'a i32>); + + #[test] + fn lifetime() { + let item = Lifetime(Bar(&1)); + + assert!(ptr::eq(item.as_ref(), item.0.as_ref())); + } + + #[derive(AsRef)] + struct FieldLifetime<'a>(#[as_ref(i32)] Bar<&'a i32>); + + #[test] + fn field_lifetime() { + let item = FieldLifetime(Bar(&1)); + + assert!(ptr::eq(item.as_ref(), item.0.as_ref())); + } } } @@ -758,6 +787,32 @@ mod single_field { assert!(ptr::eq(item.as_ref(), &item.first)); } + + #[derive(AsRef)] + #[as_ref(i32)] + struct Lifetime<'a> { + first: Bar<&'a i32>, + } + + #[test] + fn lifetime() { + let item = Lifetime { first: Bar(&1) }; + + assert!(ptr::eq(item.as_ref(), item.first.as_ref())); + } + + #[derive(AsRef)] + struct FieldLifetime<'a> { + #[as_ref(i32)] + first: Bar<&'a i32>, + } + + #[test] + fn field_lifetime() { + let item = FieldLifetime { first: Bar(&1) }; + + assert!(ptr::eq(item.as_ref(), item.first.as_ref())); + } } } } From ccf2c9a2b1d011b551a2692891484868ae7e90c3 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Thu, 31 Aug 2023 18:19:28 +0300 Subject: [PATCH 45/58] Handle lifetime params --- impl/src/as/mod.rs | 23 +++++++++++++++++------ impl/src/utils.rs | 28 ++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index b1d1c75f..6fde4237 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -15,7 +15,9 @@ use syn::{ Token, }; -use crate::utils::{forward, skip, type_contains_any_of, Either, HashSet, Spanning}; +use crate::utils::{ + contains_any_of, forward, skip, Either, HashSet, ParamSearch, Spanning, +}; /// Expands an [`AsRef`]/[`AsMut`] derive macro. pub fn expand( @@ -186,13 +188,24 @@ impl<'a> ToTokens for Expansion<'a> { let field_ref = quote! { & #mut_ self.#field_ident }; - let param_idents: HashSet<_> = self + let param_tys: HashSet<_> = self .generics .type_params() .map(|param| ¶m.ident) .collect(); - let field_contains_param = type_contains_any_of(field_ty, ¶m_idents); + let param_lfs: HashSet<_> = self + .generics + .lifetimes() + .map(|param| ¶m.lifetime.ident) + .collect(); + + let param_search = ParamSearch { + param_tys, + param_lfs, + }; + + let field_contains_param = contains_any_of(field_ty, ¶m_search); let is_blanket = matches!(&self.args, Some(AsArgs::Forward(_))); @@ -228,9 +241,7 @@ impl<'a> ToTokens for Expansion<'a> { break 'ver Direct; } - if field_contains_param - || type_contains_any_of(&return_ty, ¶m_idents) - { + if field_contains_param || contains_any_of(&return_ty, ¶m_search) { break 'ver Forwarded; } diff --git a/impl/src/utils.rs b/impl/src/utils.rs index 6ce2eb1d..0071275e 100644 --- a/impl/src/utils.rs +++ b/impl/src/utils.rs @@ -32,7 +32,7 @@ pub(crate) use self::fields_ext::FieldsExt; pub(crate) use self::spanning::Spanning; #[cfg(feature = "as_ref")] -pub(crate) use self::type_search::type_contains_any_of; +pub(crate) use self::param_search::{contains_any_of, ParamSearch}; #[derive(Clone, Copy, Default)] pub struct DeterministicState; @@ -1686,13 +1686,18 @@ mod fields_ext { } #[cfg(feature = "as_ref")] -mod type_search { +mod param_search { use syn::visit::Visit; use super::HashSet; + pub(crate) struct ParamSearch<'a> { + pub param_tys: HashSet<&'a syn::Ident>, + pub param_lfs: HashSet<&'a syn::Ident>, + } + struct TypeSearchVisitor<'a> { - search: &'a HashSet<&'a syn::Ident>, + search: &'a ParamSearch<'a>, found: bool, } @@ -1705,17 +1710,24 @@ mod type_search { if let syn::Type::Path(syn::TypePath { path, .. }) = ty { self.found = path .get_ident() - .map_or(false, |ident| self.search.contains(ident)); + .map_or(false, |ident| self.search.param_tys.contains(ident)); } syn::visit::visit_type(self, ty); } + + fn visit_lifetime(&mut self, lf: &'ast syn::Lifetime) { + if self.found { + return; + } + + self.found = self.search.param_lfs.contains(&lf.ident); + + syn::visit::visit_lifetime(self, lf) + } } - pub(crate) fn type_contains_any_of( - ty: &syn::Type, - search: &HashSet<&syn::Ident>, - ) -> bool { + pub(crate) fn contains_any_of(ty: &syn::Type, search: &ParamSearch<'_>) -> bool { let mut visitor = TypeSearchVisitor { search, found: false, From 3a5f22df9f2ff668f74d2ce8967876e5be0c5ddf Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Thu, 31 Aug 2023 18:44:05 +0300 Subject: [PATCH 46/58] Add const param tests --- tests/as_mut.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ tests/as_ref.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) diff --git a/tests/as_mut.rs b/tests/as_mut.rs index 4bbc037d..36272233 100644 --- a/tests/as_mut.rs +++ b/tests/as_mut.rs @@ -42,6 +42,12 @@ impl AsMut for Bar<&'static mut i32> { } } +impl AsMut<[i32]> for Bar<[i32; 0]> { + fn as_mut(&mut self) -> &mut [i32] { + &mut self.0 + } +} + impl Default for Bar<&'static mut i32> { fn default() -> Self { static mut STATIC: i32 = 0; @@ -394,6 +400,27 @@ mod single_field { assert!(ptr::eq(item.as_mut(), item.0.as_mut())); } + + #[derive(AsMut)] + #[as_mut([i32])] + struct ConstParam(Bar<[i32; N]>); + + #[test] + fn const_param() { + let mut item = ConstParam(Bar([])); + + assert!(ptr::eq(item.as_mut(), item.0.as_mut())); + } + + #[derive(AsMut)] + struct FieldConstParam(#[as_mut([i32])] Bar<[i32; N]>); + + #[test] + fn field_const_param() { + let mut item = FieldConstParam(Bar([])); + + assert!(ptr::eq(item.as_mut(), item.0.as_mut())); + } } } @@ -824,6 +851,32 @@ mod single_field { assert!(ptr::eq(item.as_mut(), item.first.as_mut())); } + + #[derive(AsMut)] + #[as_mut([i32])] + struct ConstParam { + first: Bar<[i32; N]>, + } + + #[test] + fn const_param() { + let mut item = ConstParam { first: Bar([]) }; + + assert!(ptr::eq(item.as_mut(), item.first.as_mut())); + } + + #[derive(AsMut)] + struct FieldConstParam { + #[as_mut([i32])] + first: Bar<[i32; N]>, + } + + #[test] + fn field_const_param() { + let mut item = FieldConstParam { first: Bar([]) }; + + assert!(ptr::eq(item.as_mut(), item.first.as_mut())); + } } } } diff --git a/tests/as_ref.rs b/tests/as_ref.rs index 184d4485..30467399 100644 --- a/tests/as_ref.rs +++ b/tests/as_ref.rs @@ -42,6 +42,12 @@ impl AsRef for Bar<&'static i32> { } } +impl AsRef<[i32]> for Bar<[i32; 0]> { + fn as_ref(&self) -> &[i32] { + &self.0 + } +} + mod single_field { use super::*; @@ -387,6 +393,27 @@ mod single_field { assert!(ptr::eq(item.as_ref(), item.0.as_ref())); } + + #[derive(AsRef)] + #[as_ref([i32])] + struct ConstParam(Bar<[i32; N]>); + + #[test] + fn const_param() { + let item = ConstParam(Bar([])); + + assert!(ptr::eq(item.as_ref(), item.0.as_ref())); + } + + #[derive(AsRef)] + struct FieldConstParam(#[as_ref([i32])] Bar<[i32; N]>); + + #[test] + fn field_const_param() { + let item = FieldConstParam(Bar([])); + + assert!(ptr::eq(item.as_ref(), item.0.as_ref())); + } } } @@ -813,6 +840,32 @@ mod single_field { assert!(ptr::eq(item.as_ref(), item.first.as_ref())); } + + #[derive(AsRef)] + #[as_ref([i32])] + struct ConstParam { + first: Bar<[i32; N]>, + } + + #[test] + fn const_param() { + let item = ConstParam { first: Bar([]) }; + + assert!(ptr::eq(item.as_ref(), item.first.as_ref())); + } + + #[derive(AsRef)] + struct FieldConstParam { + #[as_ref([i32])] + first: Bar<[i32; N]>, + } + + #[test] + fn field_const_param() { + let item = FieldConstParam { first: Bar([]) }; + + assert!(ptr::eq(item.as_ref(), item.first.as_ref())); + } } } } From 7a1f2abfa4227ec5ac8cecf183545915fb2f9177 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Thu, 31 Aug 2023 18:44:11 +0300 Subject: [PATCH 47/58] Handle const params --- impl/src/as/mod.rs | 7 +++++++ impl/src/utils.rs | 27 ++++++++++++++++++++------- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index 6fde4237..1b5d2a42 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -200,9 +200,16 @@ impl<'a> ToTokens for Expansion<'a> { .map(|param| ¶m.lifetime.ident) .collect(); + let param_consts: HashSet<_> = self + .generics + .const_params() + .map(|param| ¶m.ident) + .collect(); + let param_search = ParamSearch { param_tys, param_lfs, + param_consts, }; let field_contains_param = contains_any_of(field_ty, ¶m_search); diff --git a/impl/src/utils.rs b/impl/src/utils.rs index 0071275e..b8f92169 100644 --- a/impl/src/utils.rs +++ b/impl/src/utils.rs @@ -1694,6 +1694,7 @@ mod param_search { pub(crate) struct ParamSearch<'a> { pub param_tys: HashSet<&'a syn::Ident>, pub param_lfs: HashSet<&'a syn::Ident>, + pub param_consts: HashSet<&'a syn::Ident>, } struct TypeSearchVisitor<'a> { @@ -1702,18 +1703,17 @@ mod param_search { } impl<'a, 'ast> Visit<'ast> for TypeSearchVisitor<'a> { - fn visit_type(&mut self, ty: &'ast syn::Type) { + fn visit_type_path(&mut self, tp: &'ast syn::TypePath) { if self.found { return; } - if let syn::Type::Path(syn::TypePath { path, .. }) = ty { - self.found = path - .get_ident() - .map_or(false, |ident| self.search.param_tys.contains(ident)); - } + self.found = tp + .path + .get_ident() + .map_or(false, |ident| self.search.param_tys.contains(ident)); - syn::visit::visit_type(self, ty); + syn::visit::visit_type_path(self, tp) } fn visit_lifetime(&mut self, lf: &'ast syn::Lifetime) { @@ -1725,6 +1725,19 @@ mod param_search { syn::visit::visit_lifetime(self, lf) } + + fn visit_expr_path(&mut self, ep: &'ast syn::ExprPath) { + if self.found { + return; + } + + self.found = ep + .path + .get_ident() + .map_or(false, |ident| self.search.param_consts.contains(ident)); + + syn::visit::visit_expr_path(self, ep) + } } pub(crate) fn contains_any_of(ty: &syn::Type, search: &ParamSearch<'_>) -> bool { From 9ece99a55131a764a441ee0efe1e3f57ea9e31bc Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Fri, 1 Sep 2023 11:55:15 +0300 Subject: [PATCH 48/58] Remove unnecessary parsing --- impl/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/impl/src/lib.rs b/impl/src/lib.rs index bd6ecdc4..62c32700 100644 --- a/impl/src/lib.rs +++ b/impl/src/lib.rs @@ -56,7 +56,6 @@ mod mul_like; #[cfg(feature = "not")] mod not_like; #[cfg(any( - feature = "as_ref", feature = "debug", feature = "display", feature = "from", From df80bbe2418c997063365875bb0eb198961d361a Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Fri, 1 Sep 2023 12:23:32 +0300 Subject: [PATCH 49/58] Fix const param handling in type argument positions --- impl/src/utils.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/impl/src/utils.rs b/impl/src/utils.rs index b8f92169..62825bac 100644 --- a/impl/src/utils.rs +++ b/impl/src/utils.rs @@ -1708,10 +1708,10 @@ mod param_search { return; } - self.found = tp - .path - .get_ident() - .map_or(false, |ident| self.search.param_tys.contains(ident)); + self.found = tp.path.get_ident().map_or(false, |ident| { + self.search.param_tys.contains(ident) + || self.search.param_consts.contains(ident) + }); syn::visit::visit_type_path(self, tp) } From 0d9eeae99f82b2f95195c0e9f96b1909f8c3c0a0 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Fri, 1 Sep 2023 12:29:04 +0300 Subject: [PATCH 50/58] Improve helper naming in tests --- tests/as_mut.rs | 167 +++++++++++++++++++++++++----------------------- tests/as_ref.rs | 158 ++++++++++++++++++++++++--------------------- 2 files changed, 172 insertions(+), 153 deletions(-) diff --git a/tests/as_mut.rs b/tests/as_mut.rs index 36272233..4a57b804 100644 --- a/tests/as_mut.rs +++ b/tests/as_mut.rs @@ -10,48 +10,49 @@ use alloc::{borrow::ToOwned, collections::VecDeque, string::String, vec, vec::Ve #[cfg(feature = "std")] use std::collections::VecDeque; -use core::ptr; +use core::{marker::PhantomData, ptr}; use derive_more::AsMut; -struct Foo(i32, f64, bool); +struct Helper(i32, f64, bool); -impl AsMut for Foo { +impl AsMut for Helper { fn as_mut(&mut self) -> &mut i32 { &mut self.0 } } -impl AsMut for Foo { +impl AsMut for Helper { fn as_mut(&mut self) -> &mut f64 { &mut self.1 } } -impl AsMut for Foo { +impl AsMut for Helper { fn as_mut(&mut self) -> &mut bool { &mut self.2 } } -struct Bar(T); +struct LifetimeHelper<'a>(i32, PhantomData<&'a ()>); -impl AsMut for Bar<&'static mut i32> { - fn as_mut(&mut self) -> &mut i32 { - self.0 +impl LifetimeHelper<'static> { + fn new(val: i32) -> Self { + Self(val, PhantomData) } } -impl AsMut<[i32]> for Bar<[i32; 0]> { - fn as_mut(&mut self) -> &mut [i32] { +impl AsMut for LifetimeHelper<'static> { + fn as_mut(&mut self) -> &mut i32 { &mut self.0 } } -impl Default for Bar<&'static mut i32> { - fn default() -> Self { - static mut STATIC: i32 = 0; - Self(unsafe { &mut STATIC }) +struct ConstParamHelper([i32; N]); + +impl AsMut<[i32]> for ConstParamHelper<0> { + fn as_mut(&mut self) -> &mut [i32] { + self.0.as_mut() } } @@ -106,7 +107,7 @@ mod single_field { #[derive(AsMut)] #[as_mut(i32, f64)] - struct Types(Foo); + struct Types(Helper); // Asserts that the macro expansion doesn't generate a blanket `AsMut` // impl forwarding to the field type, by producing a trait implementations @@ -120,15 +121,15 @@ mod single_field { // Asserts that the macro expansion doesn't generate an `AsMut` impl for // the field type, by producing a trait implementations conflict error // during compilation, if it does. - impl AsMut for Types { - fn as_mut(&mut self) -> &mut Foo { + impl AsMut for Types { + fn as_mut(&mut self) -> &mut Helper { &mut self.0 } } #[test] fn types() { - let mut item = Types(Foo(1, 2.0, false)); + let mut item = Types(Helper(1, 2.0, false)); let rf: &mut i32 = item.as_mut(); assert!(ptr::eq(rf, item.0.as_mut())); @@ -138,8 +139,8 @@ mod single_field { } #[derive(AsMut)] - #[as_mut(i32, Foo)] - struct TypesWithInner(Foo); + #[as_mut(i32, Helper)] + struct TypesWithInner(Helper); // Asserts that the macro expansion doesn't generate a blanket `AsMut` // impl forwarding to the field type, by producing a trait implementations @@ -152,20 +153,20 @@ mod single_field { #[test] fn types_with_inner() { - let mut item = TypesWithInner(Foo(1, 2.0, false)); + let mut item = TypesWithInner(Helper(1, 2.0, false)); let rf: &mut i32 = item.as_mut(); assert!(ptr::eq(rf, item.0.as_mut())); - let rf: &mut Foo = item.as_mut(); + let rf: &mut Helper = item.as_mut(); assert!(ptr::eq(rf, &mut item.0)); } - type RenamedFoo = Foo; + type RenamedFoo = Helper; #[derive(AsMut)] #[as_mut(i32, RenamedFoo)] - struct TypesWithRenamedInner(Foo); + struct TypesWithRenamedInner(Helper); // Asserts that the macro expansion doesn't generate a blanket `AsMut` // impl forwarding to the field type, by producing a trait implementations @@ -179,17 +180,17 @@ mod single_field { #[test] fn types_with_renamed_inner() { - let mut item = TypesWithRenamedInner(Foo(1, 2.0, false)); + let mut item = TypesWithRenamedInner(Helper(1, 2.0, false)); let rf: &mut i32 = item.as_mut(); assert!(ptr::eq(rf, item.0.as_mut())); - let rf: &mut Foo = item.as_mut(); + let rf: &mut Helper = item.as_mut(); assert!(ptr::eq(rf, &mut item.0)); } #[derive(AsMut)] - struct FieldTypes(#[as_mut(i32, f64)] Foo); + struct FieldTypes(#[as_mut(i32, f64)] Helper); // Asserts that the macro expansion doesn't generate a blanket `AsMut` // impl forwarding to the field type, by producing a trait implementations @@ -203,15 +204,15 @@ mod single_field { // Asserts that the macro expansion doesn't generate an `AsMut` impl for // the field type, by producing a trait implementations conflict error // during compilation, if it does. - impl AsMut for FieldTypes { - fn as_mut(&mut self) -> &mut Foo { + impl AsMut for FieldTypes { + fn as_mut(&mut self) -> &mut Helper { &mut self.0 } } #[test] fn field_types() { - let mut item = FieldTypes(Foo(1, 2.0, false)); + let mut item = FieldTypes(Helper(1, 2.0, false)); let rf: &mut i32 = item.as_mut(); assert!(ptr::eq(rf, item.0.as_mut())); @@ -221,7 +222,7 @@ mod single_field { } #[derive(AsMut)] - struct FieldTypesWithInner(#[as_mut(i32, Foo)] Foo); + struct FieldTypesWithInner(#[as_mut(i32, Helper)] Helper); // Asserts that the macro expansion doesn't generate a blanket `AsMut` // impl forwarding to the field type, by producing a trait implementations @@ -234,17 +235,17 @@ mod single_field { #[test] fn field_types_with_inner() { - let mut item = FieldTypesWithInner(Foo(1, 2.0, false)); + let mut item = FieldTypesWithInner(Helper(1, 2.0, false)); let rf: &mut i32 = item.as_mut(); assert!(ptr::eq(rf, item.0.as_mut())); - let rf: &mut Foo = item.as_mut(); + let rf: &mut Helper = item.as_mut(); assert!(ptr::eq(rf, &mut item.0)); } #[derive(AsMut)] - struct FieldTypesWithRenamedInner(#[as_mut(i32, RenamedFoo)] Foo); + struct FieldTypesWithRenamedInner(#[as_mut(i32, RenamedFoo)] Helper); // Asserts that the macro expansion doesn't generate a blanket `AsMut` // impl forwarding to the field type, by producing a trait implementations @@ -257,12 +258,12 @@ mod single_field { #[test] fn field_types_with_renamed_inner() { - let mut item = FieldTypesWithRenamedInner(Foo(1, 2.0, false)); + let mut item = FieldTypesWithRenamedInner(Helper(1, 2.0, false)); let rf: &mut i32 = item.as_mut(); assert!(ptr::eq(rf, item.0.as_mut())); - let rf: &mut Foo = item.as_mut(); + let rf: &mut Helper = item.as_mut(); assert!(ptr::eq(rf, &mut item.0)); } @@ -327,7 +328,7 @@ mod single_field { #[test] fn types() { - let mut item = Types(Foo(1, 2.0, false)); + let mut item = Types(Helper(1, 2.0, false)); let rf: &mut i32 = item.as_mut(); assert!(ptr::eq(rf, item.0.as_mut())); @@ -361,7 +362,7 @@ mod single_field { #[test] fn field_types() { - let mut item = FieldTypes(Foo(1, 2.0, false)); + let mut item = FieldTypes(Helper(1, 2.0, false)); let rf: &mut i32 = item.as_mut(); assert!(ptr::eq(rf, item.0.as_mut())); @@ -382,42 +383,44 @@ mod single_field { #[derive(AsMut)] #[as_mut(i32)] - struct Lifetime<'a>(Bar<&'a mut i32>); + struct Lifetime<'a>(LifetimeHelper<'a>); #[test] fn lifetime() { - let mut item = Lifetime(Bar::default()); + let mut item = Lifetime(LifetimeHelper::new(0)); assert!(ptr::eq(item.as_mut(), item.0.as_mut())); } #[derive(AsMut)] - struct FieldLifetime<'a>(#[as_mut(i32)] Bar<&'a mut i32>); + struct FieldLifetime<'a>(#[as_mut(i32)] LifetimeHelper<'a>); #[test] fn field_lifetime() { - let mut item = FieldLifetime(Bar::default()); + let mut item = FieldLifetime(LifetimeHelper::new(0)); assert!(ptr::eq(item.as_mut(), item.0.as_mut())); } #[derive(AsMut)] #[as_mut([i32])] - struct ConstParam(Bar<[i32; N]>); + struct ConstParam(ConstParamHelper); #[test] fn const_param() { - let mut item = ConstParam(Bar([])); + let mut item = ConstParam(ConstParamHelper([])); assert!(ptr::eq(item.as_mut(), item.0.as_mut())); } #[derive(AsMut)] - struct FieldConstParam(#[as_mut([i32])] Bar<[i32; N]>); + struct FieldConstParam( + #[as_mut([i32])] ConstParamHelper, + ); #[test] fn field_const_param() { - let mut item = FieldConstParam(Bar([])); + let mut item = FieldConstParam(ConstParamHelper([])); assert!(ptr::eq(item.as_mut(), item.0.as_mut())); } @@ -491,7 +494,7 @@ mod single_field { #[derive(AsMut)] #[as_mut(i32, f64)] struct Types { - first: Foo, + first: Helper, } // Asserts that the macro expansion doesn't generate a blanket `AsMut` @@ -506,8 +509,8 @@ mod single_field { // Asserts that the macro expansion doesn't generate an `AsMut` impl for // the field type, by producing a trait implementations conflict error // during compilation, if it does. - impl AsMut for Types { - fn as_mut(&mut self) -> &mut Foo { + impl AsMut for Types { + fn as_mut(&mut self) -> &mut Helper { &mut self.first } } @@ -515,7 +518,7 @@ mod single_field { #[test] fn types() { let mut item = Types { - first: Foo(1, 2.0, false), + first: Helper(1, 2.0, false), }; let rf: &mut i32 = item.as_mut(); @@ -526,9 +529,9 @@ mod single_field { } #[derive(AsMut)] - #[as_mut(i32, Foo)] + #[as_mut(i32, Helper)] struct TypesWithInner { - first: Foo, + first: Helper, } // Asserts that the macro expansion doesn't generate a blanket `AsMut` @@ -543,22 +546,22 @@ mod single_field { #[test] fn types_with_inner() { let mut item = TypesWithInner { - first: Foo(1, 2.0, false), + first: Helper(1, 2.0, false), }; let rf: &mut i32 = item.as_mut(); assert!(ptr::eq(rf, item.first.as_mut())); - let rf: &mut Foo = item.as_mut(); + let rf: &mut Helper = item.as_mut(); assert!(ptr::eq(rf, &mut item.first)); } - type RenamedFoo = Foo; + type RenamedFoo = Helper; #[derive(AsMut)] #[as_mut(i32, RenamedFoo)] struct TypesWithRenamedInner { - first: Foo, + first: Helper, } // Asserts that the macro expansion doesn't generate a blanket `AsMut` @@ -573,20 +576,20 @@ mod single_field { #[test] fn types_with_renamed_inner() { let mut item = TypesWithRenamedInner { - first: Foo(1, 2.0, false), + first: Helper(1, 2.0, false), }; let rf: &mut i32 = item.as_mut(); assert!(ptr::eq(rf, item.first.as_mut())); - let rf: &mut Foo = item.as_mut(); + let rf: &mut Helper = item.as_mut(); assert!(ptr::eq(rf, &mut item.first)); } #[derive(AsMut)] struct FieldTypes { #[as_mut(i32, f64)] - first: Foo, + first: Helper, } // Asserts that the macro expansion doesn't generate a blanket `AsMut` @@ -601,8 +604,8 @@ mod single_field { // Asserts that the macro expansion doesn't generate an `AsMut` impl for // the field type, by producing a trait implementations conflict error // during compilation, if it does. - impl AsMut for FieldTypes { - fn as_mut(&mut self) -> &mut Foo { + impl AsMut for FieldTypes { + fn as_mut(&mut self) -> &mut Helper { &mut self.first } } @@ -610,7 +613,7 @@ mod single_field { #[test] fn field_types() { let mut item = FieldTypes { - first: Foo(1, 2.0, false), + first: Helper(1, 2.0, false), }; let rf: &mut i32 = item.as_mut(); @@ -622,8 +625,8 @@ mod single_field { #[derive(AsMut)] struct FieldTypesWithInner { - #[as_mut(i32, Foo)] - first: Foo, + #[as_mut(i32, Helper)] + first: Helper, } // Asserts that the macro expansion doesn't generate a blanket `AsMut` @@ -638,20 +641,20 @@ mod single_field { #[test] fn field_types_with_inner() { let mut item = FieldTypesWithInner { - first: Foo(1, 2.0, false), + first: Helper(1, 2.0, false), }; let rf: &mut i32 = item.as_mut(); assert!(ptr::eq(rf, item.first.as_mut())); - let rf: &mut Foo = item.as_mut(); + let rf: &mut Helper = item.as_mut(); assert!(ptr::eq(rf, &mut item.first)); } #[derive(AsMut)] struct FieldTypesWithRenamedInner { #[as_mut(i32, RenamedFoo)] - first: Foo, + first: Helper, } // Asserts that the macro expansion doesn't generate a blanket `AsMut` @@ -666,13 +669,13 @@ mod single_field { #[test] fn field_types_with_renamed_inner() { let mut item = FieldTypesWithRenamedInner { - first: Foo(1, 2.0, false), + first: Helper(1, 2.0, false), }; let rf: &mut i32 = item.as_mut(); assert!(ptr::eq(rf, item.first.as_mut())); - let rf: &mut Foo = item.as_mut(); + let rf: &mut Helper = item.as_mut(); assert!(ptr::eq(rf, &mut item.first)); } @@ -758,7 +761,7 @@ mod single_field { #[test] fn types() { let mut item = Types { - first: Foo(1, 2.0, false), + first: Helper(1, 2.0, false), }; let rf: &mut i32 = item.as_mut(); @@ -799,7 +802,7 @@ mod single_field { #[test] fn field_types() { let mut item = FieldTypes { - first: Foo(1, 2.0, false), + first: Helper(1, 2.0, false), }; let rf: &mut i32 = item.as_mut(); @@ -825,13 +828,13 @@ mod single_field { #[derive(AsMut)] #[as_mut(i32)] struct Lifetime<'a> { - first: Bar<&'a mut i32>, + first: LifetimeHelper<'a>, } #[test] fn lifetime() { let mut item = Lifetime { - first: Bar::default(), + first: LifetimeHelper::new(0), }; assert!(ptr::eq(item.as_mut(), item.first.as_mut())); @@ -840,13 +843,13 @@ mod single_field { #[derive(AsMut)] struct FieldLifetime<'a> { #[as_mut(i32)] - first: Bar<&'a mut i32>, + first: LifetimeHelper<'a>, } #[test] fn field_lifetime() { let mut item = FieldLifetime { - first: Bar::default(), + first: LifetimeHelper::new(0), }; assert!(ptr::eq(item.as_mut(), item.first.as_mut())); @@ -855,12 +858,14 @@ mod single_field { #[derive(AsMut)] #[as_mut([i32])] struct ConstParam { - first: Bar<[i32; N]>, + first: ConstParamHelper, } #[test] fn const_param() { - let mut item = ConstParam { first: Bar([]) }; + let mut item = ConstParam { + first: ConstParamHelper([]), + }; assert!(ptr::eq(item.as_mut(), item.first.as_mut())); } @@ -868,12 +873,14 @@ mod single_field { #[derive(AsMut)] struct FieldConstParam { #[as_mut([i32])] - first: Bar<[i32; N]>, + first: ConstParamHelper, } #[test] fn field_const_param() { - let mut item = FieldConstParam { first: Bar([]) }; + let mut item = FieldConstParam { + first: ConstParamHelper([]), + }; assert!(ptr::eq(item.as_mut(), item.first.as_mut())); } diff --git a/tests/as_ref.rs b/tests/as_ref.rs index 30467399..48cd9864 100644 --- a/tests/as_ref.rs +++ b/tests/as_ref.rs @@ -14,37 +14,39 @@ use core::ptr; use derive_more::AsRef; -struct Foo(i32, f64, bool); +struct Helper(i32, f64, bool); -impl AsRef for Foo { +impl AsRef for Helper { fn as_ref(&self) -> &i32 { &self.0 } } -impl AsRef for Foo { +impl AsRef for Helper { fn as_ref(&self) -> &f64 { &self.1 } } -impl AsRef for Foo { +impl AsRef for Helper { fn as_ref(&self) -> &bool { &self.2 } } -struct Bar(T); +struct LifetimeHelper<'a>(&'a i32); -impl AsRef for Bar<&'static i32> { +impl AsRef for LifetimeHelper<'static> { fn as_ref(&self) -> &i32 { self.0 } } -impl AsRef<[i32]> for Bar<[i32; 0]> { +struct ConstParamHelper([i32; N]); + +impl AsRef<[i32]> for ConstParamHelper<0> { fn as_ref(&self) -> &[i32] { - &self.0 + self.0.as_ref() } } @@ -99,7 +101,7 @@ mod single_field { #[derive(AsRef)] #[as_ref(i32, f64)] - struct Types(Foo); + struct Types(Helper); // Asserts that the macro expansion doesn't generate a blanket `AsRef` // impl forwarding to the field type, by producing a trait implementations @@ -113,15 +115,15 @@ mod single_field { // Asserts that the macro expansion doesn't generate an `AsRef` impl for // the field type, by producing a trait implementations conflict error // during compilation, if it does. - impl AsRef for Types { - fn as_ref(&self) -> &Foo { + impl AsRef for Types { + fn as_ref(&self) -> &Helper { &self.0 } } #[test] fn types() { - let item = Types(Foo(1, 2.0, false)); + let item = Types(Helper(1, 2.0, false)); let rf: &i32 = item.as_ref(); assert!(ptr::eq(rf, item.0.as_ref())); @@ -131,8 +133,8 @@ mod single_field { } #[derive(AsRef)] - #[as_ref(i32, Foo)] - struct TypesWithInner(Foo); + #[as_ref(i32, Helper)] + struct TypesWithInner(Helper); // Asserts that the macro expansion doesn't generate a blanket `AsRef` // impl forwarding to the field type, by producing a trait implementations @@ -145,20 +147,20 @@ mod single_field { #[test] fn types_with_inner() { - let item = TypesWithInner(Foo(1, 2.0, false)); + let item = TypesWithInner(Helper(1, 2.0, false)); let rf: &i32 = item.as_ref(); assert!(ptr::eq(rf, item.0.as_ref())); - let rf: &Foo = item.as_ref(); + let rf: &Helper = item.as_ref(); assert!(ptr::eq(rf, &item.0)); } - type RenamedFoo = Foo; + type RenamedFoo = Helper; #[derive(AsRef)] #[as_ref(i32, RenamedFoo)] - struct TypesWithRenamedInner(Foo); + struct TypesWithRenamedInner(Helper); // Asserts that the macro expansion doesn't generate a blanket `AsRef` // impl forwarding to the field type, by producing a trait implementations @@ -172,17 +174,17 @@ mod single_field { #[test] fn types_with_renamed_inner() { - let item = TypesWithRenamedInner(Foo(1, 2.0, false)); + let item = TypesWithRenamedInner(Helper(1, 2.0, false)); let rf: &i32 = item.as_ref(); assert!(ptr::eq(rf, item.0.as_ref())); - let rf: &Foo = item.as_ref(); + let rf: &Helper = item.as_ref(); assert!(ptr::eq(rf, &item.0)); } #[derive(AsRef)] - struct FieldTypes(#[as_ref(i32, f64)] Foo); + struct FieldTypes(#[as_ref(i32, f64)] Helper); // Asserts that the macro expansion doesn't generate a blanket `AsRef` // impl forwarding to the field type, by producing a trait implementations @@ -196,15 +198,15 @@ mod single_field { // Asserts that the macro expansion doesn't generate an `AsRef` impl for // the field type, by producing a trait implementations conflict error // during compilation, if it does. - impl AsRef for FieldTypes { - fn as_ref(&self) -> &Foo { + impl AsRef for FieldTypes { + fn as_ref(&self) -> &Helper { &self.0 } } #[test] fn field_types() { - let item = FieldTypes(Foo(1, 2.0, false)); + let item = FieldTypes(Helper(1, 2.0, false)); let rf: &i32 = item.as_ref(); assert!(ptr::eq(rf, item.0.as_ref())); @@ -214,7 +216,7 @@ mod single_field { } #[derive(AsRef)] - struct FieldTypesWithInner(#[as_ref(i32, Foo)] Foo); + struct FieldTypesWithInner(#[as_ref(i32, Helper)] Helper); // Asserts that the macro expansion doesn't generate a blanket `AsRef` // impl forwarding to the field type, by producing a trait implementations @@ -227,17 +229,17 @@ mod single_field { #[test] fn field_types_with_inner() { - let item = FieldTypesWithInner(Foo(1, 2.0, false)); + let item = FieldTypesWithInner(Helper(1, 2.0, false)); let rf: &i32 = item.as_ref(); assert!(ptr::eq(rf, item.0.as_ref())); - let rf: &Foo = item.as_ref(); + let rf: &Helper = item.as_ref(); assert!(ptr::eq(rf, &item.0)); } #[derive(AsRef)] - struct FieldTypesWithRenamedInner(#[as_ref(i32, RenamedFoo)] Foo); + struct FieldTypesWithRenamedInner(#[as_ref(i32, RenamedFoo)] Helper); // Asserts that the macro expansion doesn't generate a blanket `AsRef` // impl forwarding to the field type, by producing a trait implementations @@ -250,12 +252,12 @@ mod single_field { #[test] fn field_types_with_renamed_inner() { - let item = FieldTypesWithRenamedInner(Foo(1, 2.0, false)); + let item = FieldTypesWithRenamedInner(Helper(1, 2.0, false)); let rf: &i32 = item.as_ref(); assert!(ptr::eq(rf, item.0.as_ref())); - let rf: &Foo = item.as_ref(); + let rf: &Helper = item.as_ref(); assert!(ptr::eq(rf, &item.0)); } @@ -320,7 +322,7 @@ mod single_field { #[test] fn types() { - let item = Types(Foo(1, 2.0, false)); + let item = Types(Helper(1, 2.0, false)); let rf: &i32 = item.as_ref(); assert!(ptr::eq(rf, item.0.as_ref())); @@ -354,7 +356,7 @@ mod single_field { #[test] fn field_types() { - let item = FieldTypes(Foo(1, 2.0, false)); + let item = FieldTypes(Helper(1, 2.0, false)); let rf: &i32 = item.as_ref(); assert!(ptr::eq(rf, item.0.as_ref())); @@ -375,42 +377,44 @@ mod single_field { #[derive(AsRef)] #[as_ref(i32)] - struct Lifetime<'a>(Bar<&'a i32>); + struct Lifetime<'a>(LifetimeHelper<'a>); #[test] fn lifetime() { - let item = Lifetime(Bar(&1)); + let item = Lifetime(LifetimeHelper(&0)); assert!(ptr::eq(item.as_ref(), item.0.as_ref())); } #[derive(AsRef)] - struct FieldLifetime<'a>(#[as_ref(i32)] Bar<&'a i32>); + struct FieldLifetime<'a>(#[as_ref(i32)] LifetimeHelper<'a>); #[test] fn field_lifetime() { - let item = FieldLifetime(Bar(&1)); + let item = FieldLifetime(LifetimeHelper(&0)); assert!(ptr::eq(item.as_ref(), item.0.as_ref())); } #[derive(AsRef)] #[as_ref([i32])] - struct ConstParam(Bar<[i32; N]>); + struct ConstParam(ConstParamHelper); #[test] fn const_param() { - let item = ConstParam(Bar([])); + let item = ConstParam(ConstParamHelper([])); assert!(ptr::eq(item.as_ref(), item.0.as_ref())); } #[derive(AsRef)] - struct FieldConstParam(#[as_ref([i32])] Bar<[i32; N]>); + struct FieldConstParam( + #[as_ref([i32])] ConstParamHelper, + ); #[test] fn field_const_param() { - let item = FieldConstParam(Bar([])); + let item = FieldConstParam(ConstParamHelper([])); assert!(ptr::eq(item.as_ref(), item.0.as_ref())); } @@ -484,7 +488,7 @@ mod single_field { #[derive(AsRef)] #[as_ref(i32, f64)] struct Types { - first: Foo, + first: Helper, } // Asserts that the macro expansion doesn't generate a blanket `AsRef` @@ -499,8 +503,8 @@ mod single_field { // Asserts that the macro expansion doesn't generate an `AsRef` impl for // the field type, by producing a trait implementations conflict error // during compilation, if it does. - impl AsRef for Types { - fn as_ref(&self) -> &Foo { + impl AsRef for Types { + fn as_ref(&self) -> &Helper { &self.first } } @@ -508,7 +512,7 @@ mod single_field { #[test] fn types() { let item = Types { - first: Foo(1, 2.0, false), + first: Helper(1, 2.0, false), }; let rf: &i32 = item.as_ref(); @@ -519,9 +523,9 @@ mod single_field { } #[derive(AsRef)] - #[as_ref(i32, Foo)] + #[as_ref(i32, Helper)] struct TypesWithInner { - first: Foo, + first: Helper, } // Asserts that the macro expansion doesn't generate a blanket `AsRef` @@ -536,22 +540,22 @@ mod single_field { #[test] fn types_with_inner() { let item = TypesWithInner { - first: Foo(1, 2.0, false), + first: Helper(1, 2.0, false), }; let rf: &i32 = item.as_ref(); assert!(ptr::eq(rf, item.first.as_ref())); - let rf: &Foo = item.as_ref(); + let rf: &Helper = item.as_ref(); assert!(ptr::eq(rf, &item.first)); } - type RenamedFoo = Foo; + type RenamedFoo = Helper; #[derive(AsRef)] #[as_ref(i32, RenamedFoo)] struct TypesWithRenamedInner { - first: Foo, + first: Helper, } // Asserts that the macro expansion doesn't generate a blanket `AsRef` @@ -566,20 +570,20 @@ mod single_field { #[test] fn types_with_renamed_inner() { let item = TypesWithRenamedInner { - first: Foo(1, 2.0, false), + first: Helper(1, 2.0, false), }; let rf: &i32 = item.as_ref(); assert!(ptr::eq(rf, item.first.as_ref())); - let rf: &Foo = item.as_ref(); + let rf: &Helper = item.as_ref(); assert!(ptr::eq(rf, &item.first)); } #[derive(AsRef)] struct FieldTypes { #[as_ref(i32, f64)] - first: Foo, + first: Helper, } // Asserts that the macro expansion doesn't generate a blanket `AsRef` @@ -594,8 +598,8 @@ mod single_field { // Asserts that the macro expansion doesn't generate an `AsRef` impl for // the field type, by producing a trait implementations conflict error // during compilation, if it does. - impl AsRef for FieldTypes { - fn as_ref(&self) -> &Foo { + impl AsRef for FieldTypes { + fn as_ref(&self) -> &Helper { &self.first } } @@ -603,7 +607,7 @@ mod single_field { #[test] fn field_types() { let item = FieldTypes { - first: Foo(1, 2.0, false), + first: Helper(1, 2.0, false), }; let rf: &i32 = item.as_ref(); @@ -615,8 +619,8 @@ mod single_field { #[derive(AsRef)] struct FieldTypesWithInner { - #[as_ref(i32, Foo)] - first: Foo, + #[as_ref(i32, Helper)] + first: Helper, } // Asserts that the macro expansion doesn't generate a blanket `AsRef` @@ -631,20 +635,20 @@ mod single_field { #[test] fn field_types_with_inner() { let item = FieldTypesWithInner { - first: Foo(1, 2.0, false), + first: Helper(1, 2.0, false), }; let rf: &i32 = item.as_ref(); assert!(ptr::eq(rf, item.first.as_ref())); - let rf: &Foo = item.as_ref(); + let rf: &Helper = item.as_ref(); assert!(ptr::eq(rf, &item.first)); } #[derive(AsRef)] struct FieldTypesWithRenamedInner { #[as_ref(i32, RenamedFoo)] - first: Foo, + first: Helper, } // Asserts that the macro expansion doesn't generate a blanket `AsRef` @@ -659,13 +663,13 @@ mod single_field { #[test] fn field_types_with_renamed_inner() { let item = FieldTypesWithRenamedInner { - first: Foo(1, 2.0, false), + first: Helper(1, 2.0, false), }; let rf: &i32 = item.as_ref(); assert!(ptr::eq(rf, item.first.as_ref())); - let rf: &Foo = item.as_ref(); + let rf: &Helper = item.as_ref(); assert!(ptr::eq(rf, &item.first)); } @@ -751,7 +755,7 @@ mod single_field { #[test] fn types() { let item = Types { - first: Foo(1, 2.0, false), + first: Helper(1, 2.0, false), }; let rf: &i32 = item.as_ref(); @@ -792,7 +796,7 @@ mod single_field { #[test] fn field_types() { let item = FieldTypes { - first: Foo(1, 2.0, false), + first: Helper(1, 2.0, false), }; let rf: &i32 = item.as_ref(); @@ -818,12 +822,14 @@ mod single_field { #[derive(AsRef)] #[as_ref(i32)] struct Lifetime<'a> { - first: Bar<&'a i32>, + first: LifetimeHelper<'a>, } #[test] fn lifetime() { - let item = Lifetime { first: Bar(&1) }; + let item = Lifetime { + first: LifetimeHelper(&0), + }; assert!(ptr::eq(item.as_ref(), item.first.as_ref())); } @@ -831,12 +837,14 @@ mod single_field { #[derive(AsRef)] struct FieldLifetime<'a> { #[as_ref(i32)] - first: Bar<&'a i32>, + first: LifetimeHelper<'a>, } #[test] fn field_lifetime() { - let item = FieldLifetime { first: Bar(&1) }; + let item = FieldLifetime { + first: LifetimeHelper(&0), + }; assert!(ptr::eq(item.as_ref(), item.first.as_ref())); } @@ -844,12 +852,14 @@ mod single_field { #[derive(AsRef)] #[as_ref([i32])] struct ConstParam { - first: Bar<[i32; N]>, + first: ConstParamHelper, } #[test] fn const_param() { - let item = ConstParam { first: Bar([]) }; + let item = ConstParam { + first: ConstParamHelper([]), + }; assert!(ptr::eq(item.as_ref(), item.first.as_ref())); } @@ -857,12 +867,14 @@ mod single_field { #[derive(AsRef)] struct FieldConstParam { #[as_ref([i32])] - first: Bar<[i32; N]>, + first: ConstParamHelper, } #[test] fn field_const_param() { - let item = FieldConstParam { first: Bar([]) }; + let item = FieldConstParam { + first: ConstParamHelper([]), + }; assert!(ptr::eq(item.as_ref(), item.first.as_ref())); } From a7e0243dbbbbef53006c205f74e61a794b1f3d62 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Fri, 1 Sep 2023 12:34:08 +0300 Subject: [PATCH 51/58] Fix param handling description in docs --- impl/doc/as_mut.md | 2 +- impl/doc/as_ref.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/impl/doc/as_mut.md b/impl/doc/as_mut.md index 09cd73cf..d0e79ffe 100644 --- a/impl/doc/as_mut.md +++ b/impl/doc/as_mut.md @@ -78,7 +78,7 @@ let _: &mut [u8] = item.as_mut(); let _: &mut String = item.as_mut();_ ``` -When either the field type or the type specified to convert into contain type parameters, +When either the field type or the type specified to convert into contain generic parameters, they're compared for string equality, and when there's no match assumed to be different types. For example diff --git a/impl/doc/as_ref.md b/impl/doc/as_ref.md index ad66ca6f..1bc27ff4 100644 --- a/impl/doc/as_ref.md +++ b/impl/doc/as_ref.md @@ -78,7 +78,7 @@ let _: &[u8] = item.as_ref(); let _: &String = item.as_ref(); ``` -When either the field type or the type specified to convert into contain type parameters, +When either the field type or the type specified to convert into contain generic parameters, they're compared for string equality, and when there's no match assumed to be different types. For example From c592579d162a36768a0006a0cd40b202b4d93577 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Sat, 2 Sep 2023 00:06:48 +0300 Subject: [PATCH 52/58] Use a bound method instead of a free function for param search --- impl/src/as/mod.rs | 8 +++----- impl/src/utils.rs | 23 ++++++++++++----------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index 1b5d2a42..9ef8c1fc 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -15,9 +15,7 @@ use syn::{ Token, }; -use crate::utils::{ - contains_any_of, forward, skip, Either, HashSet, ParamSearch, Spanning, -}; +use crate::utils::{forward, skip, Either, HashSet, ParamSearch, Spanning}; /// Expands an [`AsRef`]/[`AsMut`] derive macro. pub fn expand( @@ -212,7 +210,7 @@ impl<'a> ToTokens for Expansion<'a> { param_consts, }; - let field_contains_param = contains_any_of(field_ty, ¶m_search); + let field_contains_param = param_search.any_in(field_ty); let is_blanket = matches!(&self.args, Some(AsArgs::Forward(_))); @@ -248,7 +246,7 @@ impl<'a> ToTokens for Expansion<'a> { break 'ver Direct; } - if field_contains_param || contains_any_of(&return_ty, ¶m_search) { + if field_contains_param || param_search.any_in(&return_ty) { break 'ver Forwarded; } diff --git a/impl/src/utils.rs b/impl/src/utils.rs index 62825bac..f3b2d94a 100644 --- a/impl/src/utils.rs +++ b/impl/src/utils.rs @@ -32,7 +32,7 @@ pub(crate) use self::fields_ext::FieldsExt; pub(crate) use self::spanning::Spanning; #[cfg(feature = "as_ref")] -pub(crate) use self::param_search::{contains_any_of, ParamSearch}; +pub(crate) use self::param_search::ParamSearch; #[derive(Clone, Copy, Default)] pub struct DeterministicState; @@ -1697,6 +1697,17 @@ mod param_search { pub param_consts: HashSet<&'a syn::Ident>, } + impl<'a> ParamSearch<'a> { + pub(crate) fn any_in(&self, ty: &syn::Type) -> bool { + let mut visitor = TypeSearchVisitor { + search: self, + found: false, + }; + visitor.visit_type(ty); + visitor.found + } + } + struct TypeSearchVisitor<'a> { search: &'a ParamSearch<'a>, found: bool, @@ -1739,14 +1750,4 @@ mod param_search { syn::visit::visit_expr_path(self, ep) } } - - pub(crate) fn contains_any_of(ty: &syn::Type, search: &ParamSearch<'_>) -> bool { - let mut visitor = TypeSearchVisitor { - search, - found: false, - }; - - visitor.visit_type(ty); - visitor.found - } } From 7efe4220305be9ae10e967fb3d059748cedd25e5 Mon Sep 17 00:00:00 2001 From: MegaBluejay Date: Sat, 2 Sep 2023 00:09:08 +0300 Subject: [PATCH 53/58] Remove unnecessary returns --- impl/src/utils.rs | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/impl/src/utils.rs b/impl/src/utils.rs index f3b2d94a..3d60cf83 100644 --- a/impl/src/utils.rs +++ b/impl/src/utils.rs @@ -1715,11 +1715,7 @@ mod param_search { impl<'a, 'ast> Visit<'ast> for TypeSearchVisitor<'a> { fn visit_type_path(&mut self, tp: &'ast syn::TypePath) { - if self.found { - return; - } - - self.found = tp.path.get_ident().map_or(false, |ident| { + self.found |= tp.path.get_ident().map_or(false, |ident| { self.search.param_tys.contains(ident) || self.search.param_consts.contains(ident) }); @@ -1728,21 +1724,13 @@ mod param_search { } fn visit_lifetime(&mut self, lf: &'ast syn::Lifetime) { - if self.found { - return; - } - - self.found = self.search.param_lfs.contains(&lf.ident); + self.found |= self.search.param_lfs.contains(&lf.ident); syn::visit::visit_lifetime(self, lf) } fn visit_expr_path(&mut self, ep: &'ast syn::ExprPath) { - if self.found { - return; - } - - self.found = ep + self.found |= ep .path .get_ident() .map_or(false, |ident| self.search.param_consts.contains(ident)); From 0db51e26d111ba9a24195306f75f7b0edddab5f4 Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 5 Sep 2023 18:45:46 +0300 Subject: [PATCH 54/58] Implementation bikeshedding --- impl/src/as/mod.rs | 214 ++++++++++++++++++++------------------------- impl/src/utils.rs | 44 ++++++---- src/as.rs | 34 +++++-- src/lib.rs | 6 +- 4 files changed, 156 insertions(+), 142 deletions(-) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index 9ef8c1fc..33e7c809 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -3,7 +3,7 @@ pub(crate) mod r#mut; pub(crate) mod r#ref; -use std::borrow::Cow; +use std::{borrow::Cow, iter}; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; @@ -15,7 +15,7 @@ use syn::{ Token, }; -use crate::utils::{forward, skip, Either, HashSet, ParamSearch, Spanning}; +use crate::utils::{forward, skip, Either, GenericsSearch, Spanning}; /// Expands an [`AsRef`]/[`AsMut`] derive macro. pub fn expand( @@ -67,7 +67,7 @@ pub fn expand( generics: &input.generics, field, field_index: 0, - args: Some(attr.into_inner()), + conversions: Some(attr.into_inner()), }] } else { let attrs = data @@ -113,7 +113,7 @@ pub fn expand( generics: &input.generics, field, field_index: i, - args: None, + conversions: None, }) }) .collect() @@ -130,7 +130,7 @@ pub fn expand( generics: &input.generics, field, field_index: i, - args: attr.into_args(), + conversions: attr.into_conversion_attribute(), }) } Some(FieldAttribute::Skip(_)) => unreachable!(), @@ -169,8 +169,8 @@ struct Expansion<'a> { /// Index of the [`syn::Field`]. field_index: usize, - /// Arguments specifying which conversions should be generated - args: Option, + /// Attribute specifying which conversions should be generated. + conversions: Option, } impl<'a> ToTokens for Expansion<'a> { @@ -186,104 +186,85 @@ impl<'a> ToTokens for Expansion<'a> { let field_ref = quote! { & #mut_ self.#field_ident }; - let param_tys: HashSet<_> = self - .generics - .type_params() - .map(|param| ¶m.ident) - .collect(); - - let param_lfs: HashSet<_> = self - .generics - .lifetimes() - .map(|param| ¶m.lifetime.ident) - .collect(); - - let param_consts: HashSet<_> = self - .generics - .const_params() - .map(|param| ¶m.ident) - .collect(); - - let param_search = ParamSearch { - param_tys, - param_lfs, - param_consts, + let generics_search = GenericsSearch { + types: self.generics.type_params().map(|p| &p.ident).collect(), + lifetimes: self + .generics + .lifetimes() + .map(|p| &p.lifetime.ident) + .collect(), + consts: self.generics.const_params().map(|p| &p.ident).collect(), }; + let field_contains_generics = generics_search.any_in(field_ty); - let field_contains_param = param_search.any_in(field_ty); + let is_blanket = + matches!(&self.conversions, Some(ConversionAttribute::Forward(_))); - let is_blanket = matches!(&self.args, Some(AsArgs::Forward(_))); - - let return_tys = match &self.args { - Some(AsArgs::Forward(_)) => { - Either::Left(std::iter::once(Cow::Owned(parse_quote! { __AsT }))) + let return_tys = match &self.conversions { + Some(ConversionAttribute::Forward(_)) => { + Either::Left(iter::once(Cow::Owned(parse_quote! { __AsT }))) + } + Some(ConversionAttribute::Types(tys)) => { + Either::Right(tys.iter().map(Cow::Borrowed)) } - Some(AsArgs::Types(tys)) => Either::Right(tys.iter().map(Cow::Borrowed)), - None => Either::Left(std::iter::once(Cow::Borrowed(field_ty))), + None => Either::Left(iter::once(Cow::Borrowed(field_ty))), }; for return_ty in return_tys { - use ImplKind::*; - - /// The kind of impl generated, chosen based on attribute args. + /// Kind of a generated implementation, chosen based on attribute arguments. enum ImplKind { /// Returns a reference to a field. Direct, - /// Calls `as_ref`/`as_mut` on a field + + /// Forwards `as_ref`/`as_mut` call on a field. Forwarded, - /// Uses autoderef-based specialization to determine whether - /// to use direct or forwarded based on whether the field - /// and return type match. + + /// Uses autoref-based specialization to determine whether to use direct or + /// forwarded implementation, based on whether the field and the return type match. + /// + /// Doesn't work when generics are involved. Specialized, } - let impl_kind = 'ver: { - if is_blanket { - break 'ver Forwarded; - } - - if field_ty == return_ty.as_ref() { - break 'ver Direct; - } - - if field_contains_param || param_search.any_in(&return_ty) { - break 'ver Forwarded; - } - - Specialized + let impl_kind = if is_blanket { + ImplKind::Forwarded + } else if field_ty == return_ty.as_ref() { + ImplKind::Direct + } else if field_contains_generics || generics_search.any_in(&return_ty) { + ImplKind::Forwarded + } else { + ImplKind::Specialized }; let trait_ty = quote! { ::core::convert::#trait_ident <#return_ty> }; let generics = match &impl_kind { - Forwarded => { + ImplKind::Forwarded => { let mut generics = self.generics.clone(); - generics .make_where_clause() .predicates .push(parse_quote! { #field_ty: #trait_ty }); - if is_blanket { generics .params .push(parse_quote! { #return_ty: ?::core::marker::Sized }); } - Cow::Owned(generics) } - Direct | Specialized => Cow::Borrowed(self.generics), + ImplKind::Direct | ImplKind::Specialized => { + Cow::Borrowed(self.generics) + } }; - let (impl_gens, _, where_clause) = generics.split_for_impl(); let (_, ty_gens, _) = self.generics.split_for_impl(); let body = match &impl_kind { - Direct => Cow::Borrowed(&field_ref), - Forwarded => Cow::Owned(quote! { + ImplKind::Direct => Cow::Borrowed(&field_ref), + ImplKind::Forwarded => Cow::Owned(quote! { <#field_ty as #trait_ty>::#method_ident(#field_ref) }), - Specialized => Cow::Owned(quote! { + ImplKind::Specialized => Cow::Owned(quote! { use ::derive_more::__private::ExtractRef as _; let conv = @@ -313,7 +294,36 @@ impl<'a> ToTokens for Expansion<'a> { /// #[as_ref(forward)] /// #[as_ref()] /// ``` -type StructAttribute = AsArgs; +type StructAttribute = ConversionAttribute; + +impl StructAttribute { + /// Parses a [`StructAttribute`] from the provided [`syn::Attribute`]s, preserving its [`Span`]. + /// + /// [`Span`]: proc_macro2::Span + fn parse_attrs( + attrs: &[syn::Attribute], + attr_ident: &syn::Ident, + ) -> syn::Result>> { + attrs.iter().filter(|attr| attr.path().is_ident(attr_ident)) + .try_fold(None, |attrs: Option>, attr| { + let parsed: Spanning = Spanning::new(attr.parse_args()?, attr.span()); + + if let Some(prev) = attrs { + let span = prev.span.join(parsed.span).unwrap_or(prev.span); + if let Some(new) = prev.item.merge(parsed.item) { + Ok(Some(Spanning::new(new, span))) + } else { + Err(syn::Error::new( + parsed.span, + format!("only single `#[{attr_ident}(...)]` attribute is allowed here"), + )) + } + } else { + Ok(Some(parsed)) + } + }) + } +} /// Representation of an [`AsRef`]/[`AsMut`] derive macro field attribute. /// @@ -325,7 +335,7 @@ type StructAttribute = AsArgs; /// ``` enum FieldAttribute { Empty, - Args(AsArgs), + Args(ConversionAttribute), Skip(skip::Attribute), } @@ -341,7 +351,7 @@ impl Parse for FieldAttribute { return Ok(Self::Skip(attr)); } - input.parse::().map(Self::Args) + input.parse::().map(Self::Args) } } @@ -366,7 +376,6 @@ impl FieldAttribute { attr.span(), ); - if let Some(prev) = attrs { let span = prev.span.join(parsed.span).unwrap_or(prev.span); if let Some(new) = prev.item.merge(parsed.item) { @@ -383,8 +392,8 @@ impl FieldAttribute { }) } - /// Extracts conversion arguments on the attribute, if any. - fn into_args(self) -> Option { + /// Extracts a [`ConversionAttribute`], if possible. + fn into_conversion_attribute(self) -> Option { match self { Self::Args(args) => Some(args), Self::Empty | Self::Skip(_) => None, @@ -401,34 +410,22 @@ impl FieldAttribute { } } -/// Arguments specifying which conversions should be generated. +/// Representation of an attribute, specifying which conversions should be generated. /// /// ```rust,ignore /// #[as_ref(forward)] /// #[as_ref()] /// ``` -enum AsArgs { - /// Blanket impl, fully forwarding to the field type. +enum ConversionAttribute { + /// Blanket impl, fully forwarding implementation to the one of the field type. Forward(forward::Attribute), - /// Impl for specified types, which can include both the type of the field, - /// and types for which the field type implements `AsRef` + /// Concrete specified types, which can include both the type of the field itself, and types for + /// which the field type implements [`AsRef`]. Types(Punctuated), } -impl AsArgs { - /// Merges two [`AsArgs`], if possible - fn merge(self, other: Self) -> Option { - if let (Self::Types(mut tys), Self::Types(more)) = (self, other) { - tys.extend(more); - Some(Self::Types(tys)) - } else { - None - } - } -} - -impl Parse for AsArgs { +impl Parse for ConversionAttribute { fn parse(input: ParseStream) -> syn::Result { let ahead = input.fork(); if let Ok(attr) = ahead.parse::() { @@ -442,31 +439,14 @@ impl Parse for AsArgs { } } -impl StructAttribute { - /// Parses a [`StructAttribute`] from the provided [`syn::Attribute`]s, preserving its [`Span`]. - /// - /// [`Span`]: proc_macro2::Span - fn parse_attrs( - attrs: &[syn::Attribute], - attr_ident: &syn::Ident, - ) -> syn::Result>> { - attrs.iter().filter(|attr| attr.path().is_ident(attr_ident)) - .try_fold(None, |attrs: Option>, attr| { - let parsed: Spanning = Spanning::new(attr.parse_args()?, attr.span()); - - if let Some(prev) = attrs { - let span = prev.span.join(parsed.span).unwrap_or(prev.span); - if let Some(new) = prev.item.merge(parsed.item) { - Ok(Some(Spanning::new(new, span))) - } else { - Err(syn::Error::new( - parsed.span, - format!("only single `#[{attr_ident}(...)]` attribute is allowed here"), - )) - } - } else { - Ok(Some(parsed)) - } - }) +impl ConversionAttribute { + /// Merges two [`ConversionAttribute`]s, if possible. + fn merge(self, other: Self) -> Option { + if let (Self::Types(mut tys), Self::Types(more)) = (self, other) { + tys.extend(more); + Some(Self::Types(tys)) + } else { + None + } } } diff --git a/impl/src/utils.rs b/impl/src/utils.rs index 3d60cf83..ff232560 100644 --- a/impl/src/utils.rs +++ b/impl/src/utils.rs @@ -23,6 +23,8 @@ use syn::{ pub(crate) use self::either::Either; #[cfg(any(feature = "from", feature = "into"))] pub(crate) use self::fields_ext::FieldsExt; +#[cfg(feature = "as_ref")] +pub(crate) use self::generics_search::GenericsSearch; #[cfg(any( feature = "as_ref", feature = "debug", @@ -31,9 +33,6 @@ pub(crate) use self::fields_ext::FieldsExt; ))] pub(crate) use self::spanning::Spanning; -#[cfg(feature = "as_ref")] -pub(crate) use self::param_search::ParamSearch; - #[derive(Clone, Copy, Default)] pub struct DeterministicState; @@ -1686,20 +1685,28 @@ mod fields_ext { } #[cfg(feature = "as_ref")] -mod param_search { +mod generics_search { use syn::visit::Visit; use super::HashSet; - pub(crate) struct ParamSearch<'a> { - pub param_tys: HashSet<&'a syn::Ident>, - pub param_lfs: HashSet<&'a syn::Ident>, - pub param_consts: HashSet<&'a syn::Ident>, + /// Search of whether some generics (type parameters, lifetime parameters or const parameters) + /// are present in some [`syn::Type`]. + pub(crate) struct GenericsSearch<'s> { + /// Type parameters to look for. + pub(crate) types: HashSet<&'s syn::Ident>, + + /// Lifetime parameters to look for. + pub(crate) lifetimes: HashSet<&'s syn::Ident>, + + /// Const parameters to look for. + pub(crate) consts: HashSet<&'s syn::Ident>, } - impl<'a> ParamSearch<'a> { + impl<'s> GenericsSearch<'s> { + /// Checks the provided [`syn::Type`] to contain anything from this [`GenericsSearch`]. pub(crate) fn any_in(&self, ty: &syn::Type) -> bool { - let mut visitor = TypeSearchVisitor { + let mut visitor = Visitor { search: self, found: false, }; @@ -1708,23 +1715,26 @@ mod param_search { } } - struct TypeSearchVisitor<'a> { - search: &'a ParamSearch<'a>, + /// [`Visit`]or performing a [`GenericsSearch`]. + struct Visitor<'s> { + /// [`GenericsSearch`] parameters. + search: &'s GenericsSearch<'s>, + + /// Indication whether anything was found for the [`GenericsSearch`] parameters. found: bool, } - impl<'a, 'ast> Visit<'ast> for TypeSearchVisitor<'a> { + impl<'s, 'ast> Visit<'ast> for Visitor<'s> { fn visit_type_path(&mut self, tp: &'ast syn::TypePath) { self.found |= tp.path.get_ident().map_or(false, |ident| { - self.search.param_tys.contains(ident) - || self.search.param_consts.contains(ident) + self.search.types.contains(ident) || self.search.consts.contains(ident) }); syn::visit::visit_type_path(self, tp) } fn visit_lifetime(&mut self, lf: &'ast syn::Lifetime) { - self.found |= self.search.param_lfs.contains(&lf.ident); + self.found |= self.search.lifetimes.contains(&lf.ident); syn::visit::visit_lifetime(self, lf) } @@ -1733,7 +1743,7 @@ mod param_search { self.found |= ep .path .get_ident() - .map_or(false, |ident| self.search.param_consts.contains(ident)); + .map_or(false, |ident| self.search.consts.contains(ident)); syn::visit::visit_expr_path(self, ep) } diff --git a/src/as.rs b/src/as.rs index 8997214f..d0138268 100644 --- a/src/as.rs +++ b/src/as.rs @@ -1,5 +1,13 @@ +//! Type glue for [autoref-based specialization][0], used in [`AsRef`]/[`AsMut`] macro expansion. +//! +//! Allows tp specialize the `impl AsRef for T` case over the default +//! `impl, B> AsRef for Outer` one. +//! +//! [0]: https://lukaskalbertodt.github.io/2019/12/05/generalized-autoref-based-specialization.html + use core::marker::PhantomData; +/// Container to specialize over. pub struct Conv(PhantomData<(*const Frm, *const To)>); impl Default for Conv { @@ -8,14 +16,21 @@ impl Default for Conv { } } +/// Trait performing the specialization. pub trait ExtractRef { + /// Inout reference type. type Frm; + /// Output reference type. type To; + /// Extracts the output type from the input one. fn __extract_ref(&self, frm: Self::Frm) -> Self::To; } -impl<'a, T: ?Sized> ExtractRef for &Conv<&'a T, T> { +impl<'a, T> ExtractRef for &Conv<&'a T, T> +where + T: ?Sized, +{ type Frm = &'a T; type To = &'a T; @@ -24,7 +39,11 @@ impl<'a, T: ?Sized> ExtractRef for &Conv<&'a T, T> { } } -impl<'a, Frm: ?Sized + AsRef, To: 'a + ?Sized> ExtractRef for Conv<&'a Frm, To> { +impl<'a, Frm, To> ExtractRef for Conv<&'a Frm, To> +where + Frm: AsRef + ?Sized, + To: ?Sized + 'a, +{ type Frm = &'a Frm; type To = &'a To; @@ -33,7 +52,10 @@ impl<'a, Frm: ?Sized + AsRef, To: 'a + ?Sized> ExtractRef for Conv<&'a Frm, } } -impl<'a, T: ?Sized> ExtractRef for &Conv<&'a mut T, T> { +impl<'a, T> ExtractRef for &Conv<&'a mut T, T> +where + T: ?Sized, +{ type Frm = &'a mut T; type To = &'a mut T; @@ -42,8 +64,10 @@ impl<'a, T: ?Sized> ExtractRef for &Conv<&'a mut T, T> { } } -impl<'a, Frm: ?Sized + AsMut, To: 'a + ?Sized> ExtractRef - for Conv<&'a mut Frm, To> +impl<'a, Frm, To> ExtractRef for Conv<&'a mut Frm, To> +where + Frm: AsMut + ?Sized, + To: ?Sized + 'a, { type Frm = &'a mut Frm; type To = &'a mut To; diff --git a/src/lib.rs b/src/lib.rs index e5b26f8c..93e54cc1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,14 +50,14 @@ // Not public, but exported API. For macro expansion internals only. #[doc(hidden)] pub mod __private { + #[cfg(feature = "as_ref")] + pub use crate::r#as::{Conv, ExtractRef}; + #[cfg(feature = "debug")] pub use crate::fmt::{debug_tuple, DebugTuple}; #[cfg(feature = "error")] pub use crate::vendor::thiserror::aserror::AsDynError; - - #[cfg(feature = "as_ref")] - pub use crate::r#as::{Conv, ExtractRef}; } /// Module containing macro definitions only, without corresponding traits. From 7cc8684032d1aa5a4c33e202fbaa1b60453bca31 Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 5 Sep 2023 18:58:08 +0300 Subject: [PATCH 55/58] Some tests corrections --- CHANGELOG.md | 2 +- tests/as_mut.rs | 117 +++++++++++++++++++++++------------------------- tests/as_ref.rs | 117 +++++++++++++++++++++++------------------------- 3 files changed, 113 insertions(+), 123 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 846dc1a4..b1ed08cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,7 +59,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ([#279](https://github.com/JelteF/derive_more/pull/279)) - `derive_more::derive` module exporting only macros, without traits. ([#290](https://github.com/JelteF/derive_more/pull/290)) -- Add support for selective forwarding to `AsRef`/`AsMut` derives. +- Add support for specifying concrete types to `AsRef`/`AsMut` derives. ([#298](https://github.com/JelteF/derive_more/pull/298)) ### Changed diff --git a/tests/as_mut.rs b/tests/as_mut.rs index 4a57b804..f21807fb 100644 --- a/tests/as_mut.rs +++ b/tests/as_mut.rs @@ -109,18 +109,17 @@ mod single_field { #[as_mut(i32, f64)] struct Types(Helper); - // Asserts that the macro expansion doesn't generate a blanket `AsMut` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsMut for Types { fn as_mut(&mut self) -> &mut bool { self.0.as_mut() } } - // Asserts that the macro expansion doesn't generate an `AsMut` impl for - // the field type, by producing a trait implementations conflict error - // during compilation, if it does. + // Asserts that the macro expansion doesn't generate an `AsMut` impl for the field type, by + // producing a trait implementations conflict error during compilation, if it does. impl AsMut for Types { fn as_mut(&mut self) -> &mut Helper { &mut self.0 @@ -142,9 +141,9 @@ mod single_field { #[as_mut(i32, Helper)] struct TypesWithInner(Helper); - // Asserts that the macro expansion doesn't generate a blanket `AsMut` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsMut for TypesWithInner { fn as_mut(&mut self) -> &mut bool { self.0.as_mut() @@ -168,10 +167,9 @@ mod single_field { #[as_mut(i32, RenamedFoo)] struct TypesWithRenamedInner(Helper); - // Asserts that the macro expansion doesn't generate a blanket `AsMut` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. - + // Asserts that the macro expansion doesn't generate a blanket `AsMut` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsMut for TypesWithRenamedInner { fn as_mut(&mut self) -> &mut bool { self.0.as_mut() @@ -192,18 +190,17 @@ mod single_field { #[derive(AsMut)] struct FieldTypes(#[as_mut(i32, f64)] Helper); - // Asserts that the macro expansion doesn't generate a blanket `AsMut` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsMut for FieldTypes { fn as_mut(&mut self) -> &mut bool { self.0.as_mut() } } - // Asserts that the macro expansion doesn't generate an `AsMut` impl for - // the field type, by producing a trait implementations conflict error - // during compilation, if it does. + // Asserts that the macro expansion doesn't generate an `AsMut` impl for the field type, by + // producing a trait implementations conflict error during compilation, if it does. impl AsMut for FieldTypes { fn as_mut(&mut self) -> &mut Helper { &mut self.0 @@ -224,9 +221,9 @@ mod single_field { #[derive(AsMut)] struct FieldTypesWithInner(#[as_mut(i32, Helper)] Helper); - // Asserts that the macro expansion doesn't generate a blanket `AsMut` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsMut for FieldTypesWithInner { fn as_mut(&mut self) -> &mut bool { self.0.as_mut() @@ -247,9 +244,9 @@ mod single_field { #[derive(AsMut)] struct FieldTypesWithRenamedInner(#[as_mut(i32, RenamedFoo)] Helper); - // Asserts that the macro expansion doesn't generate a blanket `AsMut` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsMut for FieldTypesWithRenamedInner { fn as_mut(&mut self) -> &mut bool { self.0.as_mut() @@ -317,9 +314,9 @@ mod single_field { #[as_mut(i32, f64)] struct Types(T); - // Asserts that the macro expansion doesn't generate a blanket `AsMut` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` impl forwarding + // to the field type, by producing a trait implementations conflict error during + // compilation, if it does. impl> AsMut for Types { fn as_mut(&mut self) -> &mut bool { self.0.as_mut() @@ -351,9 +348,9 @@ mod single_field { #[derive(AsMut)] struct FieldTypes(#[as_mut(i32, f64)] T); - // Asserts that the macro expansion doesn't generate a blanket `AsMut` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` impl forwarding + // to the field type, by producing a trait implementations conflict error during + // compilation, if it does. impl> AsMut for FieldTypes { fn as_mut(&mut self) -> &mut bool { self.0.as_mut() @@ -497,18 +494,17 @@ mod single_field { first: Helper, } - // Asserts that the macro expansion doesn't generate a blanket `AsMut` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsMut for Types { fn as_mut(&mut self) -> &mut bool { self.first.as_mut() } } - // Asserts that the macro expansion doesn't generate an `AsMut` impl for - // the field type, by producing a trait implementations conflict error - // during compilation, if it does. + // Asserts that the macro expansion doesn't generate an `AsMut` impl for the field type, by + // producing a trait implementations conflict error during compilation, if it does. impl AsMut for Types { fn as_mut(&mut self) -> &mut Helper { &mut self.first @@ -534,9 +530,9 @@ mod single_field { first: Helper, } - // Asserts that the macro expansion doesn't generate a blanket `AsMut` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsMut for TypesWithInner { fn as_mut(&mut self) -> &mut bool { self.first.as_mut() @@ -564,9 +560,9 @@ mod single_field { first: Helper, } - // Asserts that the macro expansion doesn't generate a blanket `AsMut` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsMut for TypesWithRenamedInner { fn as_mut(&mut self) -> &mut bool { self.first.as_mut() @@ -592,18 +588,17 @@ mod single_field { first: Helper, } - // Asserts that the macro expansion doesn't generate a blanket `AsMut` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsMut for FieldTypes { fn as_mut(&mut self) -> &mut bool { self.first.as_mut() } } - // Asserts that the macro expansion doesn't generate an `AsMut` impl for - // the field type, by producing a trait implementations conflict error - // during compilation, if it does. + // Asserts that the macro expansion doesn't generate an `AsMut` impl for the field type, by + // producing a trait implementations conflict error during compilation, if it does. impl AsMut for FieldTypes { fn as_mut(&mut self) -> &mut Helper { &mut self.first @@ -629,9 +624,9 @@ mod single_field { first: Helper, } - // Asserts that the macro expansion doesn't generate a blanket `AsMut` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsMut for FieldTypesWithInner { fn as_mut(&mut self) -> &mut bool { self.first.as_mut() @@ -657,9 +652,9 @@ mod single_field { first: Helper, } - // Asserts that the macro expansion doesn't generate a blanket `AsMut` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsMut for FieldTypesWithRenamedInner { fn as_mut(&mut self) -> &mut bool { self.first.as_mut() @@ -749,9 +744,9 @@ mod single_field { first: T, } - // Asserts that the macro expansion doesn't generate a blanket `AsMut` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` impl forwarding + // to the field type, by producing a trait implementations conflict error during + // compilation, if it does. impl> AsMut for Types { fn as_mut(&mut self) -> &mut bool { self.first.as_mut() @@ -790,9 +785,9 @@ mod single_field { first: T, } - // Asserts that the macro expansion doesn't generate a blanket `AsMut` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsMut` impl forwarding + // to the field type, by producing a trait implementations conflict error during + // compilation, if it does. impl> AsMut for FieldTypes { fn as_mut(&mut self) -> &mut bool { self.first.as_mut() diff --git a/tests/as_ref.rs b/tests/as_ref.rs index 48cd9864..9ab5daa7 100644 --- a/tests/as_ref.rs +++ b/tests/as_ref.rs @@ -103,18 +103,17 @@ mod single_field { #[as_ref(i32, f64)] struct Types(Helper); - // Asserts that the macro expansion doesn't generate a blanket `AsRef` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsRef for Types { fn as_ref(&self) -> &bool { self.0.as_ref() } } - // Asserts that the macro expansion doesn't generate an `AsRef` impl for - // the field type, by producing a trait implementations conflict error - // during compilation, if it does. + // Asserts that the macro expansion doesn't generate an `AsRef` impl for the field type, by + // producing a trait implementations conflict error during compilation, if it does. impl AsRef for Types { fn as_ref(&self) -> &Helper { &self.0 @@ -136,9 +135,9 @@ mod single_field { #[as_ref(i32, Helper)] struct TypesWithInner(Helper); - // Asserts that the macro expansion doesn't generate a blanket `AsRef` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsRef for TypesWithInner { fn as_ref(&self) -> &bool { self.0.as_ref() @@ -162,10 +161,9 @@ mod single_field { #[as_ref(i32, RenamedFoo)] struct TypesWithRenamedInner(Helper); - // Asserts that the macro expansion doesn't generate a blanket `AsRef` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. - + // Asserts that the macro expansion doesn't generate a blanket `AsRef` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsRef for TypesWithRenamedInner { fn as_ref(&self) -> &bool { self.0.as_ref() @@ -186,18 +184,17 @@ mod single_field { #[derive(AsRef)] struct FieldTypes(#[as_ref(i32, f64)] Helper); - // Asserts that the macro expansion doesn't generate a blanket `AsRef` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsRef for FieldTypes { fn as_ref(&self) -> &bool { self.0.as_ref() } } - // Asserts that the macro expansion doesn't generate an `AsRef` impl for - // the field type, by producing a trait implementations conflict error - // during compilation, if it does. + // Asserts that the macro expansion doesn't generate an `AsRef` impl for the field type, by + // producing a trait implementations conflict error during compilation, if it does. impl AsRef for FieldTypes { fn as_ref(&self) -> &Helper { &self.0 @@ -218,9 +215,9 @@ mod single_field { #[derive(AsRef)] struct FieldTypesWithInner(#[as_ref(i32, Helper)] Helper); - // Asserts that the macro expansion doesn't generate a blanket `AsRef` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsRef for FieldTypesWithInner { fn as_ref(&self) -> &bool { self.0.as_ref() @@ -241,9 +238,9 @@ mod single_field { #[derive(AsRef)] struct FieldTypesWithRenamedInner(#[as_ref(i32, RenamedFoo)] Helper); - // Asserts that the macro expansion doesn't generate a blanket `AsRef` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsRef for FieldTypesWithRenamedInner { fn as_ref(&self) -> &bool { self.0.as_ref() @@ -311,9 +308,9 @@ mod single_field { #[as_ref(i32, f64)] struct Types(T); - // Asserts that the macro expansion doesn't generate a blanket `AsRef` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` impl forwarding + // to the field type, by producing a trait implementations conflict error during + // compilation, if it does. impl> AsRef for Types { fn as_ref(&self) -> &bool { self.0.as_ref() @@ -345,9 +342,9 @@ mod single_field { #[derive(AsRef)] struct FieldTypes(#[as_ref(i32, f64)] T); - // Asserts that the macro expansion doesn't generate a blanket `AsRef` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` impl forwarding + // to the field type, by producing a trait implementations conflict error during + // compilation, if it does. impl> AsRef for FieldTypes { fn as_ref(&self) -> &bool { self.0.as_ref() @@ -491,18 +488,17 @@ mod single_field { first: Helper, } - // Asserts that the macro expansion doesn't generate a blanket `AsRef` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsRef for Types { fn as_ref(&self) -> &bool { self.first.as_ref() } } - // Asserts that the macro expansion doesn't generate an `AsRef` impl for - // the field type, by producing a trait implementations conflict error - // during compilation, if it does. + // Asserts that the macro expansion doesn't generate an `AsRef` impl for the field type, by + // producing a trait implementations conflict error during compilation, if it does. impl AsRef for Types { fn as_ref(&self) -> &Helper { &self.first @@ -528,9 +524,9 @@ mod single_field { first: Helper, } - // Asserts that the macro expansion doesn't generate a blanket `AsRef` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsRef for TypesWithInner { fn as_ref(&self) -> &bool { self.first.as_ref() @@ -558,9 +554,9 @@ mod single_field { first: Helper, } - // Asserts that the macro expansion doesn't generate a blanket `AsRef` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsRef for TypesWithRenamedInner { fn as_ref(&self) -> &bool { self.first.as_ref() @@ -586,18 +582,17 @@ mod single_field { first: Helper, } - // Asserts that the macro expansion doesn't generate a blanket `AsRef` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsRef for FieldTypes { fn as_ref(&self) -> &bool { self.first.as_ref() } } - // Asserts that the macro expansion doesn't generate an `AsRef` impl for - // the field type, by producing a trait implementations conflict error - // during compilation, if it does. + // Asserts that the macro expansion doesn't generate an `AsRef` impl for the field type, by + // producing a trait implementations conflict error during compilation, if it does. impl AsRef for FieldTypes { fn as_ref(&self) -> &Helper { &self.first @@ -623,9 +618,9 @@ mod single_field { first: Helper, } - // Asserts that the macro expansion doesn't generate a blanket `AsRef` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsRef for FieldTypesWithInner { fn as_ref(&self) -> &bool { self.first.as_ref() @@ -651,9 +646,9 @@ mod single_field { first: Helper, } - // Asserts that the macro expansion doesn't generate a blanket `AsRef` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` impl forwarding to + // the field type, by producing a trait implementations conflict error during compilation, + // if it does. impl AsRef for FieldTypesWithRenamedInner { fn as_ref(&self) -> &bool { self.first.as_ref() @@ -743,9 +738,9 @@ mod single_field { first: T, } - // Asserts that the macro expansion doesn't generate a blanket `AsRef` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` impl forwarding + // to the field type, by producing a trait implementations conflict error during + // compilation, if it does. impl> AsRef for Types { fn as_ref(&self) -> &bool { self.first.as_ref() @@ -784,9 +779,9 @@ mod single_field { first: T, } - // Asserts that the macro expansion doesn't generate a blanket `AsRef` - // impl forwarding to the field type, by producing a trait implementations - // conflict error during compilation, if it does. + // Asserts that the macro expansion doesn't generate a blanket `AsRef` impl forwarding + // to the field type, by producing a trait implementations conflict error during + // compilation, if it does. impl> AsRef for FieldTypes { fn as_ref(&self) -> &bool { self.first.as_ref() From e4abf7b483188aeec057959ceed66482f122e83c Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 5 Sep 2023 19:30:54 +0300 Subject: [PATCH 56/58] Docs bikeshedding --- impl/doc/as_mut.md | 94 ++++++++++++++++++++-------------------------- impl/doc/as_ref.md | 93 ++++++++++++++++++++------------------------- 2 files changed, 81 insertions(+), 106 deletions(-) diff --git a/impl/doc/as_mut.md b/impl/doc/as_mut.md index d0e79ffe..bee04f19 100644 --- a/impl/doc/as_mut.md +++ b/impl/doc/as_mut.md @@ -31,7 +31,7 @@ impl AsMut for MyWrapper { } ``` -The `#[as_mut(forward)]` attribute can be used to forward +It's also possible to use the `#[as_mut(forward)]` attribute to forward to the `as_mut` implementation of the field. So here `SingleFieldForward` implements all `AsMut` for all types that `Vec` implements `AsMut` for. @@ -61,9 +61,9 @@ where } ``` -It's also possible to specify concrete types to derive impls for with `#[as_mut()]`. - -These types can include both the type of the field, and types for which the field type implements `AsMut`. +Specifying concrete types, to derive impls for, is also supported via +`#[as_mut()]` attribute. These types can include both the type +of the field itself, and types for which the field type implements `AsMut`. ```rust # use derive_more::AsMut; @@ -78,64 +78,46 @@ let _: &mut [u8] = item.as_mut(); let _: &mut String = item.as_mut();_ ``` -When either the field type or the type specified to convert into contain generic parameters, -they're compared for string equality, and when there's no match assumed to be different types. - -For example +> **WARNING**: When either the field type, or the specified conversion type, +> contains generic parameters, they are considered as the same type only if +> are named string-equally, otherwise are assumed as different types even +> when represent the same type in fact (type aliases, for example). +> +> ```rust +> # use derive_more::AsMut; +> # +> #[derive(AsMut)] +> #[as_mut(i32)] // generates `impl> AsMut for Generic` +> struct Generic(T); +> +> #[derive(AsMut)] +> #[as_mut(T)] // generates `impl AsMut for Transparent` +> struct Transparent(T); +> +> #[derive(AsMut)] +> // #[as_mut(RenamedVec)] // not supported, as types are not named string-equally +> struct Foo(Vec); +> type RenamedVec = Vec; +> +> #[derive(AsMut)] +> #[as_mut(RenamedString)] // generates `impl AsMut for Bar`, +> struct Bar(String); // as generics are not involved +> type RenamedString = String; +> ``` + +Generating code like this is not supported: ```rust -# use derive_more::AsMut; -# -#[derive(AsMut)] -#[as_mut(i32)] struct Generic(T); -``` - -Generates a forwarded impl -```rust -# struct Generic(T); -# -impl> AsMut for Generic { +impl AsMut for Generic { fn as_mut(&mut self) -> &mut i32 { - self.0.as_mut() - } -} -``` - -And - -```rust -# use derive_more::AsMut; -# -#[derive(AsMut)] -#[as_mut(T)] -struct Generic(T); -``` - -Generates - -```rust -# struct Generic(T); -# -impl AsMut for Generic { - fn as_mut(&mut self) -> &mut T { &mut self.0 } } ``` -Generating code like this is not supported -```rust -# struct Generic(T); -# -impl AsMut for Generic { - fn as_mut(&mut self) -> &mut i32 { - &mut self.0 - } -} -``` ## Structs with Multiple Fields @@ -178,8 +160,12 @@ impl AsMut for MyWrapper { } ``` + +### Tuples (not supported) + Only conversions that use a single field are possible with this derive. -Something like this wouldn't work, due to the nature of the `AsMut` trait: +Something like this wouldn't work, due to the nature of the `AsMut` trait +itself: ```rust,compile_fail # use derive_more::AsMut @@ -189,9 +175,10 @@ Something like this wouldn't work, due to the nature of the `AsMut` trait: struct MyWrapper(String, Vec) ``` -If you need to convert into multiple references, consider using the +If you need to convert into a tuple of references, consider using the [`Into`](crate::Into) derive with `#[into(ref_mut)]`. + ### Skipping Or vice versa: you can exclude a specific field by using `#[as_mut(skip)]` (or @@ -287,6 +274,7 @@ let _: &mut [u8] = item.as_mut(); + ## Enums Deriving `AsMut` for enums is not supported. diff --git a/impl/doc/as_ref.md b/impl/doc/as_ref.md index 1bc27ff4..d6cee1d4 100644 --- a/impl/doc/as_ref.md +++ b/impl/doc/as_ref.md @@ -31,7 +31,7 @@ impl AsRef for MyWrapper { } ``` -The `#[as_ref(forward)]` attribute can be used to forward +It's also possible to use the `#[as_ref(forward)]` attribute to forward to the `as_ref` implementation of the field. So here `SingleFieldForward` implements all `AsRef` for all types that `Vec` implements `AsRef` for. @@ -61,9 +61,9 @@ where } ``` -It's also possible to specify concrete types to derive impls for with `#[as_ref()]`. - -These types can include both the type of the field, and types for which the field type implements `AsRef`. +Specifying concrete types, to derive impls for, is also supported via +`#[as_ref()]` attribute. These types can include both the type +of the field itself, and types for which the field type implements `AsRef`. ```rust # use derive_more::AsRef; @@ -78,64 +78,46 @@ let _: &[u8] = item.as_ref(); let _: &String = item.as_ref(); ``` -When either the field type or the type specified to convert into contain generic parameters, -they're compared for string equality, and when there's no match assumed to be different types. - -For example +> **WARNING**: When either the field type, or the specified conversion type, +> contains generic parameters, they are considered as the same type only if +> are named string-equally, otherwise are assumed as different types even +> when represent the same type in fact (type aliases, for example). +> +> ```rust +> # use derive_more::AsRef; +> # +> #[derive(AsRef)] +> #[as_ref(i32)] // generates `impl> AsRef for Generic` +> struct Generic(T); +> +> #[derive(AsRef)] +> #[as_ref(T)] // generates `impl AsRef for Transparent` +> struct Transparent(T); +> +> #[derive(AsRef)] +> // #[as_ref(RenamedVec)] // not supported, as types are not named string-equally +> struct Foo(Vec); +> type RenamedVec = Vec; +> +> #[derive(AsRef)] +> #[as_ref(RenamedString)] // generates `impl AsRef for Bar`, +> struct Bar(String); // as generics are not involved +> type RenamedString = String; +> ``` + +Generating code like this is not supported: ```rust -# use derive_more::AsRef; -# -#[derive(AsRef)] -#[as_ref(i32)] struct Generic(T); -``` - -Generates a forwarded impl -```rust -# struct Generic(T); -# -impl> AsRef for Generic { +impl AsRef for Generic { fn as_ref(&self) -> &i32 { - self.0.as_ref() - } -} -``` - -And - -```rust -# use derive_more::AsRef; -# -#[derive(AsRef)] -#[as_ref(T)] -struct Generic(T); -``` - -Generates - -```rust -# struct Generic(T); -# -impl AsRef for Generic { - fn as_ref(&self) -> &T { &self.0 } } ``` -Generating code like this is not supported -```rust -# struct Generic(T); -# -impl AsRef for Generic { - fn as_ref(&self) -> &i32 { - &self.0 - } -} -``` ## Structs with Multiple Fields @@ -178,8 +160,12 @@ impl AsRef for MyWrapper { } ``` + +### Tuples (not supported) + Only conversions that use a single field are possible with this derive. -Something like this wouldn't work, due to the nature of the `AsRef` trait: +Something like this wouldn't work, due to the nature of the `AsRef` trait +itself: ```rust,compile_fail # use derive_more::AsRef @@ -189,7 +175,7 @@ Something like this wouldn't work, due to the nature of the `AsRef` trait: struct MyWrapper(String, Vec) ``` -If you need to convert into multiple references, consider using the +If you need to convert into a tuple of references, consider using the [`Into`](crate::Into) derive with `#[into(ref)]`. @@ -288,6 +274,7 @@ let _: &[u8] = item.as_ref(); + ## Enums Deriving `AsRef` for enums is not supported. From b23305a2b2e7bb08d011bc18e6106203adf5e72c Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 5 Sep 2023 19:34:17 +0300 Subject: [PATCH 57/58] GitHub, are you OK? --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b126ba4b..30e5e69a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,6 +109,7 @@ jobs: steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@v1 + with: toolchain: ${{ matrix.toolchain }} From 40424cedb51d87dfdc7fbeb084641fa321836b1a Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 5 Sep 2023 19:38:08 +0300 Subject: [PATCH 58/58] And now? --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 30e5e69a..b126ba4b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,7 +109,6 @@ jobs: steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@v1 - with: toolchain: ${{ matrix.toolchain }}