diff --git a/CHANGELOG.md b/CHANGELOG.md index 87fa5252..b1ed08cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,8 @@ 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 specifying concrete types to `AsRef`/`AsMut` derives. + ([#298](https://github.com/JelteF/derive_more/pull/298)) ### Changed diff --git a/impl/Cargo.toml b/impl/Cargo.toml index 304a37ce..8fa0f8f6 100644 --- a/impl/Cargo.toml +++ b/impl/Cargo.toml @@ -48,7 +48,7 @@ default = [] add = [] add_assign = [] -as_ref = [] +as_ref = ["syn/extra-traits", "syn/visit"] constructor = [] debug = ["syn/extra-traits", "dep:unicode-xid"] deref = [] diff --git a/impl/doc/as_mut.md b/impl/doc/as_mut.md index 0be4bb44..bee04f19 100644 --- a/impl/doc/as_mut.md +++ b/impl/doc/as_mut.md @@ -46,17 +46,73 @@ 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() + } +} +``` + +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; +# +#[derive(AsMut)] +#[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();_ +``` + +> **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 +struct Generic(T); + +impl AsMut for Generic { + fn as_mut(&mut self) -> &mut i32 { + &mut self.0 } } ``` @@ -75,7 +131,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, @@ -91,9 +147,9 @@ Generates: # num: i32, # valid: bool, # } -impl AsMut for MyWrapper { +impl AsMut for MyWrapper { fn as_mut(&mut self) -> &mut String { - &mut self.name + self.name.as_mut() } } @@ -105,6 +161,24 @@ 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 +itself: + +```rust,compile_fail +# use derive_more::AsMut +# +#[derive(AsMut)] +#[as_mut((str, [u8]))] +struct MyWrapper(String, Vec) +``` + +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 @@ -176,6 +250,28 @@ struct ForwardWithOther { } ``` +Multiple forwarded impls with different 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(); +``` + diff --git a/impl/doc/as_ref.md b/impl/doc/as_ref.md index 7024034f..d6cee1d4 100644 --- a/impl/doc/as_ref.md +++ b/impl/doc/as_ref.md @@ -46,17 +46,73 @@ 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() + } +} +``` + +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; +# +#[derive(AsRef)] +#[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(); +``` + +> **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 +struct Generic(T); + +impl AsRef for Generic { + fn as_ref(&self) -> &i32 { + &self.0 } } ``` @@ -75,7 +131,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, @@ -91,9 +147,9 @@ Generates: # num: i32, # valid: bool, # } -impl AsRef for MyWrapper { - fn as_ref(&self) -> &String { - &self.name +impl AsRef for MyWrapper { + fn as_ref(&self) -> &str { + self.name.as_ref() } } @@ -105,6 +161,24 @@ 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 +itself: + +```rust,compile_fail +# use derive_more::AsRef +# +#[derive(AsRef)] +#[as_ref((str, [u8]))] +struct MyWrapper(String, Vec) +``` + +If you need to convert into a tuple of references, consider using the +[`Into`](crate::Into) derive with `#[into(ref)]`. + + ### Skipping Or vice versa: you can exclude a specific field by using `#[as_ref(skip)]` (or @@ -176,6 +250,28 @@ struct ForwardWithOther { } ``` +Multiple forwarded impls with different 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(); +``` + diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index a750aa65..33e7c809 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -3,16 +3,19 @@ pub(crate) mod r#mut; pub(crate) mod r#ref; +use std::{borrow::Cow, iter}; + use proc_macro2::TokenStream; 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::utils::{forward, skip, Either, GenericsSearch, Spanning}; /// Expands an [`AsRef`]/[`AsMut`] derive macro. pub fn expand( @@ -33,8 +36,8 @@ pub fn expand( )), }?; - let expansions = if StructAttribute::parse_attrs(&input.attrs, attr_ident)? - .is_some() + let expansions = if let Some(attr) = + StructAttribute::parse_attrs(&input.attrs, attr_ident)? { if data.fields.len() != 1 { return Err(syn::Error::new( @@ -64,7 +67,7 @@ pub fn expand( generics: &input.generics, field, field_index: 0, - forward: true, + conversions: Some(attr.into_inner()), }] } else { let attrs = data @@ -110,7 +113,7 @@ pub fn expand( generics: &input.generics, field, field_index: i, - forward: false, + conversions: None, }) }) .collect() @@ -120,14 +123,14 @@ pub fn expand( .enumerate() .zip(attrs) .filter_map(|((i, field), attr)| match attr.map(Spanning::into_inner) { - attr @ Some(FieldAttribute::Empty | FieldAttribute::Forward(_)) => { + Some(attr @ (FieldAttribute::Empty | FieldAttribute::Args(_))) => { Some(Expansion { trait_info, ident: &input.ident, generics: &input.generics, field, field_index: i, - forward: matches!(attr, Some(FieldAttribute::Forward(_))), + conversions: attr.into_conversion_attribute(), }) } Some(FieldAttribute::Skip(_)) => unreachable!(), @@ -148,7 +151,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. @@ -166,8 +169,8 @@ struct Expansion<'a> { /// Index of the [`syn::Field`]. field_index: usize, - /// Indicator whether `forward` implementation should be expanded. - forward: bool, + /// Attribute specifying which conversions should be generated. + conversions: Option, } impl<'a> ToTokens for Expansion<'a> { @@ -178,47 +181,110 @@ impl<'a> ToTokens for Expansion<'a> { Either::Left, ); - let return_ty = if self.forward { - quote! { __AsT } - } else { - quote! { #field_ty } + let (trait_ident, method_ident, mut_) = &self.trait_info; + let ty_ident = &self.ident; + + let field_ref = quote! { & #mut_ self.#field_ident }; + + 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 (trait_ident, method_ident, mut_) = &self.trait_info; - let trait_ty = quote! { ::core::convert::#trait_ident <#return_ty> }; + let is_blanket = + matches!(&self.conversions, Some(ConversionAttribute::Forward(_))); - 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 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)) + } + None => Either::Left(iter::once(Cow::Borrowed(field_ty))), + }; + + for return_ty in return_tys { + /// Kind of a generated implementation, chosen based on attribute arguments. + enum ImplKind { + /// Returns a reference to a field. + Direct, - let mut body = quote! { & #mut_ self.#field_ident }; - if self.forward { - body = quote! { - <#field_ty as #trait_ty>::#method_ident(#body) + /// Forwards `as_ref`/`as_mut` call on a field. + Forwarded, + + /// 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 = 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 { + 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) + } + 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 { + ImplKind::Direct => Cow::Borrowed(&field_ref), + ImplKind::Forwarded => Cow::Owned(quote! { + <#field_ty as #trait_ty>::#method_ident(#field_ref) + }), + ImplKind::Specialized => Cow::Owned(quote! { + use ::derive_more::__private::ExtractRef as _; + + let conv = + <::derive_more::__private::Conv<& #mut_ #field_ty, #return_ty> + as ::core::default::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 + 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); } - .to_tokens(tokens) } } @@ -226,19 +292,50 @@ impl<'a> ToTokens for Expansion<'a> { /// /// ```rust,ignore /// #[as_ref(forward)] +/// #[as_ref()] /// ``` -type StructAttribute = forward::Attribute; +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. /// /// ```rust,ignore /// #[as_ref] /// #[as_ref(forward)] +/// #[as_ref()] /// #[as_ref(skip)] #[as_ref(ignore)] /// ``` enum FieldAttribute { Empty, - Forward(forward::Attribute), + Args(ConversionAttribute), Skip(skip::Attribute), } @@ -248,22 +345,13 @@ impl Parse for FieldAttribute { 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,22 +363,90 @@ 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 - } 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"), - )) + .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); + 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(attrs) + Ok(Some(parsed)) } }) } + + /// Extracts a [`ConversionAttribute`], if possible. + fn into_conversion_attribute(self) -> Option { + match self { + Self::Args(args) => Some(args), + 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 + } + } +} + +/// Representation of an attribute, specifying which conversions should be generated. +/// +/// ```rust,ignore +/// #[as_ref(forward)] +/// #[as_ref()] +/// ``` +enum ConversionAttribute { + /// Blanket impl, fully forwarding implementation to the one of the field type. + Forward(forward::Attribute), + + /// 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 Parse for ConversionAttribute { + 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)); + } + + input + .parse_terminated(syn::Type::parse, Token![,]) + .map(Self::Types) + } +} + +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/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)) - }) + }, + ) } } diff --git a/impl/src/utils.rs b/impl/src/utils.rs index 8b5fe51c..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", @@ -1328,8 +1330,6 @@ pub(crate) mod forward { spanned::Spanned as _, }; - use super::Spanning; - /// Representation of a `forward` attribute. /// /// ```rust,ignore @@ -1345,34 +1345,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( @@ -1711,3 +1683,69 @@ mod fields_ext { impl FieldsExt for T {} } + +#[cfg(feature = "as_ref")] +mod generics_search { + use syn::visit::Visit; + + use super::HashSet; + + /// 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<'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 = Visitor { + search: self, + found: false, + }; + visitor.visit_type(ty); + visitor.found + } + } + + /// [`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<'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.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.lifetimes.contains(&lf.ident); + + syn::visit::visit_lifetime(self, lf) + } + + fn visit_expr_path(&mut self, ep: &'ast syn::ExprPath) { + self.found |= ep + .path + .get_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 new file mode 100644 index 00000000..d0138268 --- /dev/null +++ b/src/as.rs @@ -0,0 +1,78 @@ +//! 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 { + fn default() -> Self { + Self(PhantomData) + } +} + +/// 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> ExtractRef for &Conv<&'a T, T> +where + T: ?Sized, +{ + type Frm = &'a T; + type To = &'a T; + + fn __extract_ref(&self, frm: Self::Frm) -> Self::To { + frm + } +} + +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; + + fn __extract_ref(&self, frm: Self::Frm) -> Self::To { + frm.as_ref() + } +} + +impl<'a, T> ExtractRef for &Conv<&'a mut T, T> +where + T: ?Sized, +{ + type Frm = &'a mut T; + type To = &'a mut T; + + fn __extract_ref(&self, frm: Self::Frm) -> Self::To { + frm + } +} + +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; + + fn __extract_ref(&self, frm: Self::Frm) -> Self::To { + frm.as_mut() + } +} diff --git a/src/lib.rs b/src/lib.rs index e38053a8..93e54cc1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,6 +50,9 @@ // 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}; @@ -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; diff --git a/tests/as_mut.rs b/tests/as_mut.rs index 846ab117..f21807fb 100644 --- a/tests/as_mut.rs +++ b/tests/as_mut.rs @@ -10,10 +10,52 @@ 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 Helper(i32, f64, bool); + +impl AsMut for Helper { + fn as_mut(&mut self) -> &mut i32 { + &mut self.0 + } +} + +impl AsMut for Helper { + fn as_mut(&mut self) -> &mut f64 { + &mut self.1 + } +} + +impl AsMut for Helper { + fn as_mut(&mut self) -> &mut bool { + &mut self.2 + } +} + +struct LifetimeHelper<'a>(i32, PhantomData<&'a ()>); + +impl LifetimeHelper<'static> { + fn new(val: i32) -> Self { + Self(val, PhantomData) + } +} + +impl AsMut for LifetimeHelper<'static> { + fn as_mut(&mut self) -> &mut i32 { + &mut self.0 + } +} + +struct ConstParamHelper([i32; N]); + +impl AsMut<[i32]> for ConstParamHelper<0> { + fn as_mut(&mut self) -> &mut [i32] { + self.0.as_mut() + } +} + mod single_field { use super::*; @@ -63,6 +105,165 @@ mod single_field { assert!(ptr::eq(rf, item.0.as_mut())); } + #[derive(AsMut)] + #[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. + 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. + impl AsMut for Types { + fn as_mut(&mut self) -> &mut Helper { + &mut self.0 + } + } + + #[test] + fn types() { + let mut item = Types(Helper(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)] + #[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. + impl AsMut for TypesWithInner { + fn as_mut(&mut self) -> &mut bool { + self.0.as_mut() + } + } + + #[test] + fn types_with_inner() { + 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 Helper = item.as_mut(); + assert!(ptr::eq(rf, &mut item.0)); + } + + type RenamedFoo = Helper; + + #[derive(AsMut)] + #[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. + 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(Helper(1, 2.0, false)); + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.0.as_mut())); + + let rf: &mut Helper = item.as_mut(); + assert!(ptr::eq(rf, &mut item.0)); + } + + #[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. + 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. + impl AsMut for FieldTypes { + fn as_mut(&mut self) -> &mut Helper { + &mut self.0 + } + } + + #[test] + fn field_types() { + let mut item = FieldTypes(Helper(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 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. + 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(Helper(1, 2.0, false)); + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.0.as_mut())); + + let rf: &mut Helper = item.as_mut(); + assert!(ptr::eq(rf, &mut item.0)); + } + + #[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. + 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(Helper(1, 2.0, false)); + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.0.as_mut())); + + let rf: &mut Helper = item.as_mut(); + assert!(ptr::eq(rf, &mut item.0)); + } + mod generic { use super::*; @@ -108,6 +309,118 @@ 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 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() + } + } + + #[test] + fn types() { + let mut item = Types(Helper(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)] + #[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); + + // 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() + } + } + + #[test] + fn field_types() { + let mut item = FieldTypes(Helper(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 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)); + } + + #[derive(AsMut)] + #[as_mut(i32)] + struct Lifetime<'a>(LifetimeHelper<'a>); + + #[test] + fn lifetime() { + 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)] LifetimeHelper<'a>); + + #[test] + fn field_lifetime() { + let mut item = FieldLifetime(LifetimeHelper::new(0)); + + assert!(ptr::eq(item.as_mut(), item.0.as_mut())); + } + + #[derive(AsMut)] + #[as_mut([i32])] + struct ConstParam(ConstParamHelper); + + #[test] + fn const_param() { + let mut item = ConstParam(ConstParamHelper([])); + + assert!(ptr::eq(item.as_mut(), item.0.as_mut())); + } + + #[derive(AsMut)] + struct FieldConstParam( + #[as_mut([i32])] ConstParamHelper, + ); + + #[test] + fn field_const_param() { + let mut item = FieldConstParam(ConstParamHelper([])); + + assert!(ptr::eq(item.as_mut(), item.0.as_mut())); + } } } @@ -175,6 +488,192 @@ mod single_field { assert!(ptr::eq(rf, item.first.as_mut())); } + #[derive(AsMut)] + #[as_mut(i32, f64)] + struct Types { + 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. + 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. + impl AsMut for Types { + fn as_mut(&mut self) -> &mut Helper { + &mut self.first + } + } + + #[test] + fn types() { + let mut item = Types { + first: Helper(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)] + #[as_mut(i32, Helper)] + struct TypesWithInner { + 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. + 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: Helper(1, 2.0, false), + }; + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.first.as_mut())); + + let rf: &mut Helper = item.as_mut(); + assert!(ptr::eq(rf, &mut item.first)); + } + + type RenamedFoo = Helper; + + #[derive(AsMut)] + #[as_mut(i32, RenamedFoo)] + struct TypesWithRenamedInner { + 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. + 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: Helper(1, 2.0, false), + }; + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.first.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: 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. + 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. + impl AsMut for FieldTypes { + fn as_mut(&mut self) -> &mut Helper { + &mut self.first + } + } + + #[test] + fn field_types() { + let mut item = FieldTypes { + first: Helper(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 FieldTypesWithInner { + #[as_mut(i32, Helper)] + 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. + 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: Helper(1, 2.0, false), + }; + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.first.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: 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. + 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: Helper(1, 2.0, false), + }; + + let rf: &mut i32 = item.as_mut(); + assert!(ptr::eq(rf, item.first.as_mut())); + + let rf: &mut Helper = item.as_mut(); + assert!(ptr::eq(rf, &mut item.first)); + } + mod generic { use super::*; @@ -238,6 +737,148 @@ 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 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() + } + } + + #[test] + fn types() { + let mut item = Types { + first: Helper(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)] + #[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)] + 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. + impl> AsMut for FieldTypes { + fn as_mut(&mut self) -> &mut bool { + self.first.as_mut() + } + } + + #[test] + fn field_types() { + let mut item = FieldTypes { + first: Helper(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 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)); + } + + #[derive(AsMut)] + #[as_mut(i32)] + struct Lifetime<'a> { + first: LifetimeHelper<'a>, + } + + #[test] + fn lifetime() { + let mut item = Lifetime { + first: LifetimeHelper::new(0), + }; + + assert!(ptr::eq(item.as_mut(), item.first.as_mut())); + } + + #[derive(AsMut)] + struct FieldLifetime<'a> { + #[as_mut(i32)] + first: LifetimeHelper<'a>, + } + + #[test] + fn field_lifetime() { + let mut item = FieldLifetime { + first: LifetimeHelper::new(0), + }; + + assert!(ptr::eq(item.as_mut(), item.first.as_mut())); + } + + #[derive(AsMut)] + #[as_mut([i32])] + struct ConstParam { + first: ConstParamHelper, + } + + #[test] + fn const_param() { + let mut item = ConstParam { + first: ConstParamHelper([]), + }; + + assert!(ptr::eq(item.as_mut(), item.first.as_mut())); + } + + #[derive(AsMut)] + struct FieldConstParam { + #[as_mut([i32])] + first: ConstParamHelper, + } + + #[test] + fn field_const_param() { + let mut item = FieldConstParam { + first: ConstParamHelper([]), + }; + + assert!(ptr::eq(item.as_mut(), item.first.as_mut())); + } } } } @@ -308,6 +949,36 @@ mod multi_field { assert!(ptr::eq(rf, item.0.as_mut())); } + type RenamedString = String; + + #[derive(AsMut)] + 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. + 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]); + + 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())); + } + mod generic { use super::*; @@ -354,6 +1025,50 @@ 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())); + } + + #[derive(AsMut)] + struct TypesWithInner( + #[as_mut(Vec, [T])] Vec, + #[as_mut(str)] U, + ); + + #[test] + fn types_with_inner() { + let mut item = TypesWithInner(vec![1i32], "a".to_owned()); + + 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())); + } + + #[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())); + } } } @@ -452,6 +1167,41 @@ mod multi_field { assert!(ptr::eq(rf, item.first.as_mut())); } + type RenamedString = String; + + #[derive(AsMut)] + struct Types { + #[as_mut(str, RenamedString)] + 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 Vec { + &mut self.second + } + } + + #[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 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())); + } + mod generic { use super::*; @@ -530,6 +1280,70 @@ 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())); + } + + #[derive(AsMut)] + struct TypesWithInner { + #[as_mut(Vec, [T])] + first: Vec, + #[as_mut(str)] + second: U, + } + + #[test] + fn types_inner() { + let mut item = TypesWithInner { + first: vec![1i32], + second: "a".to_owned(), + }; + + 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())); + } + + #[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 35512452..9ab5daa7 100644 --- a/tests/as_ref.rs +++ b/tests/as_ref.rs @@ -14,6 +14,42 @@ use core::ptr; use derive_more::AsRef; +struct Helper(i32, f64, bool); + +impl AsRef for Helper { + fn as_ref(&self) -> &i32 { + &self.0 + } +} + +impl AsRef for Helper { + fn as_ref(&self) -> &f64 { + &self.1 + } +} + +impl AsRef for Helper { + fn as_ref(&self) -> &bool { + &self.2 + } +} + +struct LifetimeHelper<'a>(&'a i32); + +impl AsRef for LifetimeHelper<'static> { + fn as_ref(&self) -> &i32 { + self.0 + } +} + +struct ConstParamHelper([i32; N]); + +impl AsRef<[i32]> for ConstParamHelper<0> { + fn as_ref(&self) -> &[i32] { + self.0.as_ref() + } +} + mod single_field { use super::*; @@ -63,6 +99,165 @@ mod single_field { assert!(ptr::eq(rf, item.0.as_ref())); } + #[derive(AsRef)] + #[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. + 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. + impl AsRef for Types { + fn as_ref(&self) -> &Helper { + &self.0 + } + } + + #[test] + fn types() { + let item = Types(Helper(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)] + #[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. + impl AsRef for TypesWithInner { + fn as_ref(&self) -> &bool { + self.0.as_ref() + } + } + + #[test] + fn types_with_inner() { + let item = TypesWithInner(Helper(1, 2.0, false)); + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.0.as_ref())); + + let rf: &Helper = item.as_ref(); + assert!(ptr::eq(rf, &item.0)); + } + + type RenamedFoo = Helper; + + #[derive(AsRef)] + #[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. + impl AsRef for TypesWithRenamedInner { + fn as_ref(&self) -> &bool { + self.0.as_ref() + } + } + + #[test] + fn types_with_renamed_inner() { + let item = TypesWithRenamedInner(Helper(1, 2.0, false)); + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.0.as_ref())); + + let rf: &Helper = item.as_ref(); + assert!(ptr::eq(rf, &item.0)); + } + + #[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. + 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. + impl AsRef for FieldTypes { + fn as_ref(&self) -> &Helper { + &self.0 + } + } + + #[test] + fn field_types() { + let item = FieldTypes(Helper(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 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. + impl AsRef for FieldTypesWithInner { + fn as_ref(&self) -> &bool { + self.0.as_ref() + } + } + + #[test] + fn field_types_with_inner() { + let item = FieldTypesWithInner(Helper(1, 2.0, false)); + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.0.as_ref())); + + let rf: &Helper = item.as_ref(); + assert!(ptr::eq(rf, &item.0)); + } + + #[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. + impl AsRef for FieldTypesWithRenamedInner { + fn as_ref(&self) -> &bool { + self.0.as_ref() + } + } + + #[test] + fn field_types_with_renamed_inner() { + let item = FieldTypesWithRenamedInner(Helper(1, 2.0, false)); + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.0.as_ref())); + + let rf: &Helper = item.as_ref(); + assert!(ptr::eq(rf, &item.0)); + } + mod generic { use super::*; @@ -108,6 +303,118 @@ 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 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() + } + } + + #[test] + fn types() { + let item = Types(Helper(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)] + #[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); + + // 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() + } + } + + #[test] + fn field_types() { + let item = FieldTypes(Helper(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 FieldTypesInner(#[as_ref(Vec)] Vec); + + #[test] + fn field_types_inner() { + let item = FieldTypesInner(vec![1i32]); + + assert!(ptr::eq(item.as_ref(), &item.0)); + } + + #[derive(AsRef)] + #[as_ref(i32)] + struct Lifetime<'a>(LifetimeHelper<'a>); + + #[test] + fn lifetime() { + let item = Lifetime(LifetimeHelper(&0)); + + assert!(ptr::eq(item.as_ref(), item.0.as_ref())); + } + + #[derive(AsRef)] + struct FieldLifetime<'a>(#[as_ref(i32)] LifetimeHelper<'a>); + + #[test] + fn field_lifetime() { + let item = FieldLifetime(LifetimeHelper(&0)); + + assert!(ptr::eq(item.as_ref(), item.0.as_ref())); + } + + #[derive(AsRef)] + #[as_ref([i32])] + struct ConstParam(ConstParamHelper); + + #[test] + fn const_param() { + let item = ConstParam(ConstParamHelper([])); + + assert!(ptr::eq(item.as_ref(), item.0.as_ref())); + } + + #[derive(AsRef)] + struct FieldConstParam( + #[as_ref([i32])] ConstParamHelper, + ); + + #[test] + fn field_const_param() { + let item = FieldConstParam(ConstParamHelper([])); + + assert!(ptr::eq(item.as_ref(), item.0.as_ref())); + } } } @@ -175,6 +482,192 @@ mod single_field { assert!(ptr::eq(rf, item.first.as_ref())); } + #[derive(AsRef)] + #[as_ref(i32, f64)] + struct Types { + 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. + 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. + impl AsRef for Types { + fn as_ref(&self) -> &Helper { + &self.first + } + } + + #[test] + fn types() { + let item = Types { + first: Helper(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)] + #[as_ref(i32, Helper)] + struct TypesWithInner { + 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. + impl AsRef for TypesWithInner { + fn as_ref(&self) -> &bool { + self.first.as_ref() + } + } + + #[test] + fn types_with_inner() { + let item = TypesWithInner { + first: Helper(1, 2.0, false), + }; + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.first.as_ref())); + + let rf: &Helper = item.as_ref(); + assert!(ptr::eq(rf, &item.first)); + } + + type RenamedFoo = Helper; + + #[derive(AsRef)] + #[as_ref(i32, RenamedFoo)] + struct TypesWithRenamedInner { + 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. + impl AsRef for TypesWithRenamedInner { + fn as_ref(&self) -> &bool { + self.first.as_ref() + } + } + + #[test] + fn types_with_renamed_inner() { + let item = TypesWithRenamedInner { + first: Helper(1, 2.0, false), + }; + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.first.as_ref())); + + let rf: &Helper = item.as_ref(); + assert!(ptr::eq(rf, &item.first)); + } + + #[derive(AsRef)] + struct FieldTypes { + #[as_ref(i32, f64)] + 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. + 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. + impl AsRef for FieldTypes { + fn as_ref(&self) -> &Helper { + &self.first + } + } + + #[test] + fn field_types() { + let item = FieldTypes { + first: Helper(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 FieldTypesWithInner { + #[as_ref(i32, Helper)] + 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. + impl AsRef for FieldTypesWithInner { + fn as_ref(&self) -> &bool { + self.first.as_ref() + } + } + + #[test] + fn field_types_with_inner() { + let item = FieldTypesWithInner { + first: Helper(1, 2.0, false), + }; + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.first.as_ref())); + + let rf: &Helper = item.as_ref(); + assert!(ptr::eq(rf, &item.first)); + } + + #[derive(AsRef)] + struct FieldTypesWithRenamedInner { + #[as_ref(i32, RenamedFoo)] + 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. + impl AsRef for FieldTypesWithRenamedInner { + fn as_ref(&self) -> &bool { + self.first.as_ref() + } + } + + #[test] + fn field_types_with_renamed_inner() { + let item = FieldTypesWithRenamedInner { + first: Helper(1, 2.0, false), + }; + + let rf: &i32 = item.as_ref(); + assert!(ptr::eq(rf, item.first.as_ref())); + + let rf: &Helper = item.as_ref(); + assert!(ptr::eq(rf, &item.first)); + } + mod generic { use super::*; @@ -238,6 +731,148 @@ 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 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() + } + } + + #[test] + fn types() { + let item = Types { + first: Helper(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)] + #[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)] + 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. + impl> AsRef for FieldTypes { + fn as_ref(&self) -> &bool { + self.first.as_ref() + } + } + + #[test] + fn field_types() { + let item = FieldTypes { + first: Helper(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 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)); + } + + #[derive(AsRef)] + #[as_ref(i32)] + struct Lifetime<'a> { + first: LifetimeHelper<'a>, + } + + #[test] + fn lifetime() { + let item = Lifetime { + first: LifetimeHelper(&0), + }; + + assert!(ptr::eq(item.as_ref(), item.first.as_ref())); + } + + #[derive(AsRef)] + struct FieldLifetime<'a> { + #[as_ref(i32)] + first: LifetimeHelper<'a>, + } + + #[test] + fn field_lifetime() { + let item = FieldLifetime { + first: LifetimeHelper(&0), + }; + + assert!(ptr::eq(item.as_ref(), item.first.as_ref())); + } + + #[derive(AsRef)] + #[as_ref([i32])] + struct ConstParam { + first: ConstParamHelper, + } + + #[test] + fn const_param() { + let item = ConstParam { + first: ConstParamHelper([]), + }; + + assert!(ptr::eq(item.as_ref(), item.first.as_ref())); + } + + #[derive(AsRef)] + struct FieldConstParam { + #[as_ref([i32])] + first: ConstParamHelper, + } + + #[test] + fn field_const_param() { + let item = FieldConstParam { + first: ConstParamHelper([]), + }; + + assert!(ptr::eq(item.as_ref(), item.first.as_ref())); + } } } } @@ -308,6 +943,36 @@ mod multi_field { assert!(ptr::eq(rf, item.0.as_ref())); } + type RenamedString = String; + + #[derive(AsRef)] + 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. + impl AsRef> for Types { + fn as_ref(&self) -> &Vec { + &self.1 + } + } + + #[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: &String = item.as_ref(); + assert!(ptr::eq(rf, &item.0)); + + let rf: &[u8] = item.as_ref(); + assert!(ptr::eq(rf, item.1.as_ref())); + } + mod generic { use super::*; @@ -354,6 +1019,50 @@ 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())); + } + + #[derive(AsRef)] + struct TypesWithInner( + #[as_ref(Vec, [T])] Vec, + #[as_ref(str)] U, + ); + + #[test] + fn types_with_inner() { + let item = TypesWithInner(vec![1i32], "a".to_owned()); + + 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())); + } + + #[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())); + } } } @@ -452,6 +1161,41 @@ mod multi_field { assert!(ptr::eq(rf, item.first.as_ref())); } + type RenamedString = String; + + #[derive(AsRef)] + struct Types { + #[as_ref(str, RenamedString)] + first: String, + #[as_ref([u8])] + second: 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. + impl AsRef> for Types { + fn as_ref(&self) -> &Vec { + &self.second + } + } + + #[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: &String = item.as_ref(); + assert!(ptr::eq(rf, &item.first)); + + let rf: &[u8] = item.as_ref(); + assert!(ptr::eq(rf, item.second.as_ref())); + } + mod generic { use super::*; @@ -530,6 +1274,70 @@ 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())); + } + + #[derive(AsRef)] + struct TypesWithInner { + #[as_ref(Vec, [T])] + first: Vec, + #[as_ref(str)] + second: U, + } + + #[test] + fn types_with_inner() { + let item = TypesWithInner { + first: vec![1i32], + second: "a".to_owned(), + }; + + 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())); + } + + #[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())); + } } } } 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/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..f0838843 --- /dev/null +++ b/tests/compile_fail/as_mut/renamed_generic.stderr @@ -0,0 +1,21 @@ +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 + = 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_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_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/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..22fd78a5 --- /dev/null +++ b/tests/compile_fail/as_ref/renamed_generic.stderr @@ -0,0 +1,21 @@ +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 + = 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` 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 | | } + | |_^ 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