diff --git a/CHANGELOG.md b/CHANGELOG.md index 833502c2..f910cc2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Add `Hash` derive similar to `std`'s one, but considering generics correctly, and supporting custom hash functions per field or skipping fields. ([#532](https://github.com/JelteF/derive_more/pull/532)) +- Add `Borrow` derive for single-field structs. +- Add `BorrowMut` derive for single-field structs. ### Fixed diff --git a/Cargo.toml b/Cargo.toml index 5e9c4cc1..48f9e65a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,7 @@ default = ["std"] add = ["derive_more-impl/add"] add_assign = ["derive_more-impl/add_assign"] as_ref = ["derive_more-impl/as_ref"] +borrow = ["derive_more-impl/borrow"] constructor = ["derive_more-impl/constructor"] debug = ["derive_more-impl/debug"] deref = ["derive_more-impl/deref"] @@ -85,6 +86,7 @@ full = [ "add", "add_assign", "as_ref", + "borrow", "constructor", "debug", "deref", @@ -132,6 +134,16 @@ name = "as_ref" path = "tests/as_ref.rs" required-features = ["as_ref"] +[[test]] +name = "borrow" +path = "tests/borrow.rs" +required-features = ["borrow"] + +[[test]] +name = "borrow_mut" +path = "tests/borrow_mut.rs" +required-features = ["borrow"] + [[test]] name = "boats_display_derive" path = "tests/boats_display_derive.rs" @@ -260,7 +272,24 @@ required-features = ["unwrap"] [[test]] name = "compile_fail" path = "tests/compile_fail/mod.rs" -required-features = ["as_ref", "debug", "display", "from", "into", "is_variant", "try_from", "try_into"] +required-features = [ + "add", + "add_assign", + "as_ref", + "borrow", + "debug", + "display", + "eq", + "from", + "from_str", + "hash", + "into", + "is_variant", + "mul", + "mul_assign", + "try_from", + "try_into", +] [[test]] name = "no_std" diff --git a/README.md b/README.md index 2d2a2601..ad6f3aec 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ These are traits that are used to convert automatically between types. 5. [`TryInto`] 6. [`IntoIterator`] 7. [`AsRef`], [`AsMut`] +8. [`Borrow`], [`BorrowMut`] ### Formatting traits @@ -250,6 +251,8 @@ Changing [MSRV] (minimum supported Rust version) of this crate is treated as a * [`IntoIterator`]: https://docs.rs/derive_more/latest/derive_more/derive.IntoIterator.html [`AsRef`]: https://docs.rs/derive_more/latest/derive_more/derive.AsRef.html [`AsMut`]: https://docs.rs/derive_more/latest/derive_more/derive.AsMut.html +[`Borrow`]: https://docs.rs/derive_more/latest/derive_more/derive.Borrow.html +[`BorrowMut`]: https://docs.rs/derive_more/latest/derive_more/derive.BorrowMut.html [`Debug`]: https://docs.rs/derive_more/latest/derive_more/derive.Debug.html [`Display`-like]: https://docs.rs/derive_more/latest/derive_more/derive.Display.html diff --git a/impl/Cargo.toml b/impl/Cargo.toml index cfd82220..1cbbec1e 100644 --- a/impl/Cargo.toml +++ b/impl/Cargo.toml @@ -52,6 +52,7 @@ default = [] add = ["syn/extra-traits", "syn/visit"] add_assign = ["syn/extra-traits", "syn/visit"] as_ref = ["syn/extra-traits", "syn/visit"] +borrow = ["syn/visit"] constructor = [] debug = ["syn/extra-traits", "dep:unicode-ident"] deref = [] @@ -80,6 +81,7 @@ full = [ "add", "add_assign", "as_ref", + "borrow", "constructor", "debug", "deref", diff --git a/impl/doc/as_mut.md b/impl/doc/as_mut.md index bee04f19..cbeeb6b3 100644 --- a/impl/doc/as_mut.md +++ b/impl/doc/as_mut.md @@ -69,13 +69,12 @@ of the field itself, and types for which the field type implements `AsMut`. # use derive_more::AsMut; # #[derive(AsMut)] -#[as_mut(str, [u8], String)] +#[as_mut(str, 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();_ +let _: &mut String = item.as_mut(); ``` > **WARNING**: When either the field type, or the specified conversion type, @@ -148,7 +147,7 @@ Generates: # valid: bool, # } impl AsMut for MyWrapper { - fn as_mut(&mut self) -> &mut String { + fn as_mut(&mut self) -> &mut str { self.name.as_mut() } } diff --git a/impl/doc/borrow.md b/impl/doc/borrow.md new file mode 100644 index 00000000..6f83c49c --- /dev/null +++ b/impl/doc/borrow.md @@ -0,0 +1,90 @@ +# What `#[derive(Borrow)]` generates + +Deriving `Borrow` generates an implementation of `core::borrow::Borrow` that +borrows a single-field struct as its field. + +`Borrow` has stronger semantic requirements than `AsRef`: equality, ordering and +hashing of the borrowed value are expected to match those of the owning value. +For that reason, this derive only supports structs with exactly one field. + + + + +## Newtypes and Structs with One Field + +When `Borrow` is derived for a newtype or a struct with one field, a single +implementation is generated for the field type. + +```rust +# use derive_more::Borrow; +# +#[derive(Borrow)] +struct MyWrapper(String); +``` + +Generates code equivalent to: + +```rust +# struct MyWrapper(String); +impl core::borrow::Borrow for MyWrapper { + fn borrow(&self) -> &String { + &self.0 + } +} +``` + +It's also possible to use the `#[borrow(forward)]` attribute to forward to the +field's `Borrow` implementation. + +```rust +# use derive_more::Borrow; +# use derive_more::core::borrow::Borrow as _; +# +#[derive(Borrow)] +#[borrow(forward)] +struct MyWrapper(String); + +let item = MyWrapper("test".to_owned()); +let _: &str = item.borrow(); +``` + +This generates code equivalent to: + +```rust +# struct MyWrapper(String); +impl core::borrow::Borrow for MyWrapper +where + String: core::borrow::Borrow, +{ + #[inline] + fn borrow(&self) -> &T { + self.0.borrow() + } +} +``` + +Forwarding cannot be derived through a generic parameter, an associated type of +a generic parameter, or a forwarding pointer to either. Directly borrowing an +associated type of a generic parameter is not supported either. These shapes can +overlap with `core`'s blanket `impl Borrow for T`. + + + + +## Structs with Multiple Fields + +Deriving `Borrow` for structs with more than one field is not supported. + +```rust,compile_fail +# use derive_more::Borrow; +# +#[derive(Borrow)] +struct User(String, bool); +``` + + + + +## Enums + +Deriving `Borrow` for enums is not supported. diff --git a/impl/doc/borrow_mut.md b/impl/doc/borrow_mut.md new file mode 100644 index 00000000..f8738880 --- /dev/null +++ b/impl/doc/borrow_mut.md @@ -0,0 +1,108 @@ +# What `#[derive(BorrowMut)]` generates + +Deriving `BorrowMut` generates an implementation of +`core::borrow::BorrowMut` that mutably borrows a single-field struct as its +field. + +`BorrowMut` requires `Borrow`, so the type must also implement the +matching `Borrow` implementation. Usually this means deriving both `Borrow` and +`BorrowMut`. + + + + +## Newtypes and Structs with One Field + +When `BorrowMut` is derived for a newtype or a struct with one field, a single +implementation is generated for the field type. + +```rust +# use derive_more::{Borrow, BorrowMut}; +# +#[derive(Borrow, BorrowMut)] +struct MyWrapper(String); +``` + +Generates code equivalent to: + +```rust +# struct MyWrapper(String); +# impl core::borrow::Borrow for MyWrapper { +# fn borrow(&self) -> &String { +# &self.0 +# } +# } +impl core::borrow::BorrowMut for MyWrapper { + fn borrow_mut(&mut self) -> &mut String { + &mut self.0 + } +} +``` + +It's also possible to use the `#[borrow_mut(forward)]` attribute to forward to +the field's `BorrowMut` implementation. The matching `Borrow` implementation +must borrow the same type, so forwarded `BorrowMut` usually goes together with +`#[borrow(forward)]`. + +```rust +# use derive_more::{Borrow, BorrowMut}; +# use derive_more::core::borrow::BorrowMut as _; +# +#[derive(Borrow, BorrowMut)] +#[borrow(forward)] +#[borrow_mut(forward)] +struct MyWrapper(String); + +let mut item = MyWrapper("test".to_owned()); +let _: &mut str = item.borrow_mut(); +``` + +This generates code equivalent to: + +```rust +# struct MyWrapper(String); +# impl core::borrow::Borrow for MyWrapper +# where +# String: core::borrow::Borrow, +# { +# #[inline] +# fn borrow(&self) -> &T { +# self.0.borrow() +# } +# } +impl core::borrow::BorrowMut for MyWrapper +where + String: core::borrow::BorrowMut, +{ + #[inline] + fn borrow_mut(&mut self) -> &mut T { + self.0.borrow_mut() + } +} +``` + +Forwarding cannot be derived through a generic parameter, an associated type of +a generic parameter, or a forwarding pointer to either. Directly borrowing an +associated type of a generic parameter is not supported either. These shapes can +overlap with `core`'s blanket `impl BorrowMut for T`. + + + + +## Structs with Multiple Fields + +Deriving `BorrowMut` for structs with more than one field is not supported. + +```rust,compile_fail +# use derive_more::{Borrow, BorrowMut}; +# +#[derive(Borrow, BorrowMut)] +struct User(String, bool); +``` + + + + +## Enums + +Deriving `BorrowMut` for enums is not supported. diff --git a/impl/src/borrow.rs b/impl/src/borrow.rs new file mode 100644 index 00000000..abf19209 --- /dev/null +++ b/impl/src/borrow.rs @@ -0,0 +1,444 @@ +//! Implementation of `Borrow`/`BorrowMut` derive macros. + +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use syn::{ + parse::{Parse, ParseStream}, + parse_quote, + spanned::Spanned as _, +}; + +use crate::utils::{ + add_extra_generic_type_param, + attr::{self, ParseMultiple as _}, + Either, GenericsSearch, Spanning, +}; + +/// Expands a `Borrow`/`BorrowMut` derive macro. +pub(crate) fn expand( + input: &syn::DeriveInput, + trait_name: &'static str, +) -> syn::Result { + let trait_ident = format_ident!("{trait_name}"); + let is_mutable = trait_name == "BorrowMut"; + let attr_name = + format_ident!("{}", if is_mutable { "borrow_mut" } else { "borrow" }); + let trait_info = ExpansionCtx { + trait_ident: &trait_ident, + attr_name: &attr_name, + is_mutable, + }; + + let data = match &input.data { + syn::Data::Struct(data) => Ok(data), + syn::Data::Enum(e) => Err(syn::Error::new( + e.enum_token.span(), + format!("`{trait_ident}` cannot be derived for enums"), + )), + syn::Data::Union(u) => Err(syn::Error::new( + u.union_token.span(), + format!("`{trait_ident}` cannot be derived for unions"), + )), + }?; + + if data.fields.len() != 1 { + return Err(syn::Error::new( + if data.fields.is_empty() { + data.struct_token.span + } else { + data.fields.span() + }, + format!("`{trait_ident}` can only be derived for structs with exactly one field"), + )); + } + + let field = data.fields.iter().next().unwrap(); + let struct_attr = StructAttribute::parse_attrs(&input.attrs, trait_info.attr_name)?; + let field_attr = FieldAttribute::parse_attrs(&field.attrs, trait_info.attr_name)?; + + if struct_attr.is_some() && field_attr.is_some() { + return Err(syn::Error::new( + field.span(), + format!( + "`#[{}(...)]` cannot be placed on both struct and its field", + trait_info.attr_name, + ), + )); + } + + let is_forwarded = if struct_attr.is_some() { + true + } else if let Some(attr) = field_attr { + match attr.item { + FieldAttribute::Direct(_) => false, + FieldAttribute::Forward(_) => true, + FieldAttribute::Skip(skip) => { + return Err(syn::Error::new( + attr.span, + format!( + "`#[{}({})]` cannot be used when deriving `{}` \ + because exactly one field must be borrowed", + trait_info.attr_name, + skip.name(), + trait_info.trait_ident, + ), + )); + } + } + } else { + false + }; + + validate_field_type(field, &input.generics, is_forwarded, trait_info)?; + + Ok(expand_impl( + &input.ident, + &input.generics, + field, + is_forwarded, + trait_info, + )) +} + +fn validate_field_type( + field: &syn::Field, + generics: &syn::Generics, + is_forwarded: bool, + trait_info: ExpansionCtx<'_>, +) -> syn::Result<()> { + let field_ty = &field.ty; + + if is_assoc_type_involving_generic_param(field_ty, generics) { + return Err(syn::Error::new( + field_ty.span(), + format!( + "`{}` cannot be derived for an associated type projection involving generic \ + parameters because the impl can overlap with `core`'s blanket `{}` \ + implementation", + trait_info.trait_ident, + trait_info.trait_ident, + ), + )); + } + + if is_forwarded && is_forwarded_overlap(field_ty, generics) { + return Err(syn::Error::new( + field_ty.span(), + format!( + "`#[{}(forward)]` cannot be used on a generic parameter field, an associated \ + type projection involving generic parameters, or a forwarding pointer to one \ + because the impl can overlap with `core`'s blanket `{}` implementation", + trait_info.attr_name, + trait_info.trait_ident, + ), + )); + } + + Ok(()) +} + +fn is_assoc_type_involving_generic_param( + ty: &syn::Type, + generics: &syn::Generics, +) -> bool { + let ty = match ty { + syn::Type::Group(ty) => { + return is_assoc_type_involving_generic_param(&ty.elem, generics) + } + syn::Type::Paren(ty) => { + return is_assoc_type_involving_generic_param(&ty.elem, generics) + } + syn::Type::Path(ty) => ty, + _ => return false, + }; + + if ty.qself.is_some() { + return GenericsSearch::from(generics).any_in(&syn::Type::Path(ty.clone())); + } + + ty.path.segments.len() > 1 + && generics + .type_params() + .any(|param| ty.path.segments[0].ident == param.ident) +} + +fn is_bare_generic_param(ty: &syn::Type, generics: &syn::Generics) -> bool { + let syn::Type::Path(ty) = ty else { + return false; + }; + + ty.qself.is_none() + && ty.path.segments.len() == 1 + && generics + .type_params() + .any(|param| ty.path.segments[0].ident == param.ident) +} + +fn is_forwarded_overlap(ty: &syn::Type, generics: &syn::Generics) -> bool { + let ty = peel_forwarding_pointer(ty); + is_bare_generic_param(ty, generics) + || is_assoc_type_involving_generic_param(ty, generics) +} + +fn peel_forwarding_pointer(ty: &syn::Type) -> &syn::Type { + match ty { + syn::Type::Group(ty) => peel_forwarding_pointer(&ty.elem), + syn::Type::Paren(ty) => peel_forwarding_pointer(&ty.elem), + syn::Type::Reference(ty) => peel_forwarding_pointer(&ty.elem), + syn::Type::Path(type_path) => { + fundamental_path_inner_type(type_path).map_or(ty, peel_forwarding_pointer) + } + _ => ty, + } +} + +fn fundamental_path_inner_type(ty: &syn::TypePath) -> Option<&syn::Type> { + let segment = fundamental_path_segment(ty)?; + + let syn::PathArguments::AngleBracketed(arguments) = &segment.arguments else { + return None; + }; + + arguments.args.iter().find_map(|arg| { + if let syn::GenericArgument::Type(ty) = arg { + Some(ty) + } else { + None + } + }) +} + +fn fundamental_path_segment(ty: &syn::TypePath) -> Option<&syn::PathSegment> { + if ty.qself.is_some() { + return None; + } + + let segments = ty.path.segments.iter().collect::>(); + match segments.as_slice() { + [segment] if segment.ident == "Box" || segment.ident == "Pin" => Some(segment), + [std_or_alloc, boxed, segment] + if (std_or_alloc.ident == "std" || std_or_alloc.ident == "alloc") + && boxed.ident == "boxed" + && segment.ident == "Box" => + { + Some(segment) + } + [std_or_core, pin, segment] + if (std_or_core.ident == "std" || std_or_core.ident == "core") + && pin.ident == "pin" + && segment.ident == "Pin" => + { + Some(segment) + } + [derive_more, core, pin, segment] + if derive_more.ident == "derive_more" + && core.ident == "core" + && pin.ident == "pin" + && segment.ident == "Pin" => + { + Some(segment) + } + _ => None, + } +} + +#[derive(Clone, Copy)] +struct ExpansionCtx<'a> { + trait_ident: &'a syn::Ident, + attr_name: &'a syn::Ident, + is_mutable: bool, +} + +fn expand_impl( + ty_ident: &syn::Ident, + generics: &syn::Generics, + field: &syn::Field, + is_forwarded: bool, + trait_info: ExpansionCtx<'_>, +) -> TokenStream { + let field_ty = &field.ty; + let field_ident = field + .ident + .as_ref() + .map_or_else(|| quote! { 0 }, |ident| quote! { #ident }); + let (_, ty_gens, _) = generics.split_for_impl(); + let borrow_ty = extra_borrow_type_param(generics, trait_info); + let trait_ident = trait_info.trait_ident; + let method_ident = trait_info.attr_name; + let mutability = trait_info.is_mutable.then(|| quote! { mut }); + + let trait_ty = if is_forwarded { + quote! { #borrow_ty } + } else { + quote! { #field_ty } + }; + let return_ty = if is_forwarded { + quote! { #trait_ty } + } else { + borrowed_type_for_return(field_ty) + }; + let trait_path = quote! { + derive_more::core::borrow::#trait_ident<#trait_ty> + }; + + let generics = if is_forwarded { + let mut generics = add_extra_generic_type_param( + generics, + quote! { #borrow_ty: ?derive_more::core::marker::Sized }, + ); + generics.make_where_clause().predicates.push(parse_quote! { + #field_ty: #trait_path + }); + generics + } else { + generics.clone() + }; + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + let body = if is_forwarded { + quote! { + <#field_ty as #trait_path>::#method_ident(& #mutability self.#field_ident) + } + } else { + quote! { + & #mutability self.#field_ident + } + }; + + quote! { + #[allow(deprecated)] // omit warnings on deprecated fields/variants + #[allow(unreachable_code)] // omit warnings for `!` and other unreachable types + #[automatically_derived] + impl #impl_gens #trait_path for #ty_ident #ty_gens #where_clause { + #[inline] + fn #method_ident(& #mutability self) -> & #mutability #return_ty { + #body + } + } + } +} + +fn extra_borrow_type_param( + generics: &syn::Generics, + trait_info: ExpansionCtx<'_>, +) -> syn::Ident { + let prefix = if trait_info.is_mutable { + "__BorrowMutT" + } else { + "__BorrowT" + }; + let mut ident = format_ident!("{prefix}"); + let mut index = 0; + + while generics.params.iter().any(|param| match param { + syn::GenericParam::Type(param) => param.ident == ident, + syn::GenericParam::Const(param) => param.ident == ident, + syn::GenericParam::Lifetime(_) => false, + }) { + index += 1; + ident = format_ident!("{prefix}{index}"); + } + + ident +} + +fn borrowed_type_for_return(ty: &syn::Type) -> TokenStream { + match ty { + syn::Type::Group(ty) => borrowed_type_for_return(&ty.elem), + syn::Type::Paren(ty) => borrowed_type_for_return(&ty.elem), + syn::Type::TraitObject(ty) => { + let mut ty = ty.clone(); + if !ty + .bounds + .iter() + .any(|bound| matches!(bound, syn::TypeParamBound::Lifetime(_))) + { + ty.bounds.push(parse_quote! { 'static }); + } + + quote! { (#ty) } + } + _ => quote! { #ty }, + } +} + +type StructAttribute = attr::Forward; + +#[derive(Clone, Copy)] +enum FieldAttribute { + Direct(attr::Empty), + Forward(attr::Forward), + Skip(attr::Skip), +} + +type UntypedFieldAttribute = Either>; + +impl From for FieldAttribute { + fn from(value: UntypedFieldAttribute) -> Self { + match value { + UntypedFieldAttribute::Left(empty) => Self::Direct(empty), + UntypedFieldAttribute::Right(Either::Left(forward)) => { + Self::Forward(forward) + } + UntypedFieldAttribute::Right(Either::Right(skip)) => Self::Skip(skip), + } + } +} + +impl From for UntypedFieldAttribute { + fn from(value: FieldAttribute) -> Self { + match value { + FieldAttribute::Direct(empty) => Self::Left(empty), + FieldAttribute::Forward(forward) => Self::Right(Either::Left(forward)), + FieldAttribute::Skip(skip) => Self::Right(Either::Right(skip)), + } + } +} + +impl Parse for FieldAttribute { + fn parse(input: ParseStream<'_>) -> syn::Result { + mod ident { + use syn::custom_keyword; + + custom_keyword!(forward); + custom_keyword!(skip); + custom_keyword!(ignore); + } + + let ahead = input.lookahead1(); + + if ahead.peek(ident::forward) { + input.parse::().map(Self::Forward) + } else if ahead.peek(ident::skip) || ahead.peek(ident::ignore) { + input.parse::().map(Self::Skip) + } else { + Err(ahead.error()) + } + } +} + +impl attr::ParseMultiple for FieldAttribute { + fn parse_attr_with( + attr: &syn::Attribute, + parser: &P, + ) -> syn::Result { + if matches!(attr.meta, syn::Meta::Path(_)) { + Ok(Self::Direct(attr::Empty)) + } else { + attr.parse_args_with(|ps: ParseStream<'_>| parser.parse(ps)) + } + } + + fn merge_attrs( + prev: Spanning, + new: Spanning, + name: &syn::Ident, + ) -> syn::Result> { + UntypedFieldAttribute::merge_attrs( + prev.map(Into::into), + new.map(Into::into), + name, + ) + .map(|attr| attr.map(Self::from)) + } +} diff --git a/impl/src/lib.rs b/impl/src/lib.rs index 0133ed05..ef083775 100644 --- a/impl/src/lib.rs +++ b/impl/src/lib.rs @@ -12,6 +12,8 @@ mod utils; #[cfg(feature = "as_ref")] mod r#as; +#[cfg(feature = "borrow")] +mod borrow; #[cfg(feature = "eq")] mod cmp; #[cfg(feature = "constructor")] @@ -90,14 +92,20 @@ impl Output for Result { } macro_rules! create_derive( - ($feature:literal, $mod_:ident $(:: $mod_rest:ident)*, $trait_:ident, $fn_name: ident $(,$attribute:ident)* $(,)?) => { + (@impl $feature:literal, $doc:literal, $mod_:ident $(:: $mod_rest:ident)*, $trait_:ident, $fn_name: ident $(,$attribute:ident)* $(,)?) => { #[cfg(feature = $feature)] #[proc_macro_derive($trait_, attributes($($attribute),*))] - #[doc = include_str!(concat!("../doc/", $feature, ".md"))] + #[doc = include_str!(concat!("../doc/", $doc, ".md"))] pub fn $fn_name(input: TokenStream) -> TokenStream { let ast = syn::parse(input).unwrap(); Output::process($mod_$(:: $mod_rest)*::expand(&ast, stringify!($trait_))) } + }; + ($feature:literal, doc = $doc:literal, $mod_:ident $(:: $mod_rest:ident)*, $trait_:ident, $fn_name: ident $(,$attribute:ident)* $(,)?) => { + create_derive!(@impl $feature, $doc, $mod_$(:: $mod_rest)*, $trait_, $fn_name $(,$attribute)*); + }; + ($feature:literal, $mod_:ident $(:: $mod_rest:ident)*, $trait_:ident, $fn_name: ident $(,$attribute:ident)* $(,)?) => { + create_derive!(@impl $feature, $feature, $mod_$(:: $mod_rest)*, $trait_, $fn_name $(,$attribute)*); } ); @@ -143,9 +151,26 @@ create_derive!( bitxor_assign, ); -create_derive!("as_ref", r#as::r#mut, AsMut, as_mut_derive, as_mut); +create_derive!( + "as_ref", + doc = "as_mut", + r#as::r#mut, + AsMut, + as_mut_derive, + as_mut +); create_derive!("as_ref", r#as::r#ref, AsRef, as_ref_derive, as_ref); +create_derive!("borrow", borrow, Borrow, borrow_derive, borrow); +create_derive!( + "borrow", + doc = "borrow_mut", + borrow, + BorrowMut, + borrow_mut_derive, + borrow_mut, +); + create_derive!("constructor", constructor, Constructor, constructor_derive); create_derive!("debug", fmt::debug, Debug, debug_derive, debug); diff --git a/impl/src/utils.rs b/impl/src/utils.rs index 82178bb1..bf0d77a5 100644 --- a/impl/src/utils.rs +++ b/impl/src/utils.rs @@ -16,6 +16,7 @@ use syn::{ feature = "add", feature = "add_assign", feature = "as_ref", + feature = "borrow", feature = "debug", feature = "display", feature = "eq", @@ -35,6 +36,7 @@ pub(crate) use self::fields_ext::FieldsExt; feature = "add", feature = "add_assign", feature = "as_ref", + feature = "borrow", feature = "eq", feature = "from_str", feature = "hash", @@ -46,6 +48,7 @@ pub(crate) use self::generics_search::GenericsSearch; feature = "add", feature = "add_assign", feature = "as_ref", + feature = "borrow", feature = "debug", feature = "display", feature = "eq", @@ -1329,6 +1332,7 @@ pub fn is_type_parameter_used_in_type( feature = "add", feature = "add_assign", feature = "as_ref", + feature = "borrow", feature = "debug", feature = "display", feature = "eq", @@ -1408,6 +1412,7 @@ mod either { feature = "add", feature = "add_assign", feature = "as_ref", + feature = "borrow", feature = "debug", feature = "display", feature = "eq", @@ -1511,6 +1516,7 @@ mod spanning { feature = "add", feature = "add_assign", feature = "as_ref", + feature = "borrow", feature = "debug", feature = "display", feature = "eq", @@ -1535,6 +1541,7 @@ pub(crate) mod attr { #[cfg(any( feature = "as_ref", + feature = "borrow", feature = "from", feature = "into", feature = "try_from" @@ -1544,6 +1551,7 @@ pub(crate) mod attr { pub(crate) use self::error::Error; #[cfg(any( feature = "as_ref", + feature = "borrow", feature = "from", feature = "mul", feature = "mul_assign", @@ -1555,6 +1563,7 @@ pub(crate) mod attr { feature = "add", feature = "add_assign", feature = "as_ref", + feature = "borrow", feature = "debug", feature = "eq", feature = "from", @@ -1704,6 +1713,7 @@ pub(crate) mod attr { #[cfg(any( feature = "as_ref", + feature = "borrow", feature = "from", feature = "into", feature = "try_from" @@ -1767,6 +1777,7 @@ pub(crate) mod attr { #[cfg(any( feature = "as_ref", + feature = "borrow", feature = "from", feature = "mul", feature = "mul_assign", @@ -1897,6 +1908,7 @@ pub(crate) mod attr { feature = "add", feature = "add_assign", feature = "as_ref", + feature = "borrow", feature = "debug", feature = "display", feature = "eq", @@ -2563,6 +2575,7 @@ mod fields_ext { feature = "add", feature = "add_assign", feature = "as_ref", + feature = "borrow", feature = "eq", feature = "from_str", feature = "hash", diff --git a/src/lib.rs b/src/lib.rs index cd763273..39e0d3e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,8 @@ //! [`TryInto`]: macro@crate::TryInto //! [`IntoIterator`]: macro@crate::IntoIterator //! [`AsRef`]: macro@crate::AsRef +//! [`Borrow`]: macro@crate::Borrow +//! [`BorrowMut`]: macro@crate::BorrowMut //! //! [`Debug`]: macro@crate::Debug //! [`Display`-like]: macro@crate::Display @@ -184,6 +186,7 @@ pub mod with_trait { SubAssign, ); re_export_traits!("as_ref", as_ref_traits, core::convert, AsMut, AsRef); + re_export_traits!("borrow", borrow_traits, core::borrow, Borrow, BorrowMut); re_export_traits!("debug", debug_traits, core::fmt, Debug); re_export_traits!("deref", deref_traits, core::ops, Deref); re_export_traits!("deref_mut", deref_mut_traits, core::ops, DerefMut); @@ -259,6 +262,9 @@ pub mod with_trait { #[cfg(feature = "as_ref")] pub use derive_more_impl::{AsMut, AsRef}; + #[cfg(feature = "borrow")] + pub use derive_more_impl::{Borrow, BorrowMut}; + #[cfg(feature = "constructor")] pub use derive_more_impl::Constructor; @@ -352,6 +358,10 @@ pub mod with_trait { #[doc(hidden)] pub use all_traits_and_derives::{AsMut, AsRef}; + #[cfg(feature = "borrow")] + #[doc(hidden)] + pub use all_traits_and_derives::{Borrow, BorrowMut}; + #[cfg(feature = "constructor")] #[doc(hidden)] pub use all_traits_and_derives::Constructor; @@ -460,6 +470,7 @@ pub mod with_trait { feature = "add", feature = "add_assign", feature = "as_ref", + feature = "borrow", feature = "constructor", feature = "debug", feature = "deref", diff --git a/tests/borrow.rs b/tests/borrow.rs new file mode 100644 index 00000000..5cafc8ac --- /dev/null +++ b/tests/borrow.rs @@ -0,0 +1,319 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(nightly, feature(never_type))] +#![allow(dead_code)] // some code is tested for type checking only + +#[cfg(not(feature = "std"))] +extern crate alloc; + +#[cfg(not(feature = "std"))] +use alloc::{borrow::ToOwned, string::String, vec, vec::Vec}; + +#[cfg(feature = "std")] +use std::borrow::ToOwned; + +use core::{borrow::Borrow as _, ptr}; + +use derive_more::Borrow; + +mod single_field { + use super::*; + + #[test] + fn tuple() { + #[derive(Borrow)] + struct Tuple(String); + + let item = Tuple("test".to_owned()); + let borrowed: &String = item.borrow(); + + assert!(ptr::eq(borrowed, &item.0)); + } + + #[test] + fn named() { + #[derive(Borrow)] + struct Named { + value: String, + } + + let item = Named { + value: "test".to_owned(), + }; + let borrowed: &String = item.borrow(); + + assert!(ptr::eq(borrowed, &item.value)); + } + + #[test] + fn generic() { + #[derive(Borrow)] + struct Generic(T); + + let item = Generic("test".to_owned()); + let borrowed: &String = item.borrow(); + + assert!(ptr::eq(borrowed, &item.0)); + } + + #[test] + fn field() { + #[derive(Borrow)] + struct Field(#[borrow] String); + + let item = Field("test".to_owned()); + let borrowed: &String = item.borrow(); + + assert!(ptr::eq(borrowed, &item.0)); + } + + #[test] + fn named_field() { + #[derive(Borrow)] + struct NamedField { + #[borrow] + value: String, + } + + let item = NamedField { + value: "test".to_owned(), + }; + let borrowed: &String = item.borrow(); + + assert!(ptr::eq(borrowed, &item.value)); + } + + #[test] + fn lifetime() { + #[derive(Borrow)] + struct Lifetime<'a>(&'a i32); + + let value = 1; + let item = Lifetime(&value); + let borrowed: &&i32 = item.borrow(); + + assert!(ptr::eq::(*borrowed, item.0)); + } + + #[test] + fn field_lifetime() { + #[derive(Borrow)] + struct FieldLifetime<'a>(#[borrow] &'a i32); + + let value = 1; + let item = FieldLifetime(&value); + let borrowed: &&i32 = item.borrow(); + + assert!(ptr::eq::(*borrowed, item.0)); + } + + #[test] + fn named_lifetime() { + #[derive(Borrow)] + struct NamedLifetime<'a> { + value: &'a i32, + } + + let value = 1; + let item = NamedLifetime { value: &value }; + let borrowed: &&i32 = item.borrow(); + + assert!(ptr::eq::(*borrowed, item.value)); + } + + #[test] + fn named_field_lifetime() { + #[derive(Borrow)] + struct NamedFieldLifetime<'a> { + #[borrow] + value: &'a i32, + } + + let value = 1; + let item = NamedFieldLifetime { value: &value }; + let borrowed: &&i32 = item.borrow(); + + assert!(ptr::eq::(*borrowed, item.value)); + } + + #[test] + fn const_param() { + #[derive(Borrow)] + struct ConstParam([i32; N]); + + let item = ConstParam([1, 2]); + let borrowed: &[i32; 2] = item.borrow(); + + assert!(ptr::eq(borrowed, &item.0)); + } + + #[test] + fn named_const_param() { + #[derive(Borrow)] + struct NamedConstParam { + value: [i32; N], + } + + let item = NamedConstParam { value: [1, 2] }; + let borrowed: &[i32; 2] = item.borrow(); + + assert!(ptr::eq(borrowed, &item.value)); + } + + #[test] + fn field_const_param() { + #[derive(Borrow)] + struct FieldConstParam(#[borrow] [i32; N]); + + let item = FieldConstParam([1, 2]); + let borrowed: &[i32; 2] = item.borrow(); + + assert!(ptr::eq(borrowed, &item.0)); + } + + #[test] + fn named_field_const_param() { + #[derive(Borrow)] + struct NamedFieldConstParam { + #[borrow] + value: [i32; N], + } + + let item = NamedFieldConstParam { value: [1, 2] }; + let borrowed: &[i32; 2] = item.borrow(); + + assert!(ptr::eq(borrowed, &item.value)); + } + + #[test] + fn forward() { + #[derive(Borrow)] + #[borrow(forward)] + struct Forward(String); + + let item = Forward("test".to_owned()); + let borrowed: &str = item.borrow(); + + assert!(ptr::eq::(borrowed, item.0.borrow())); + } + + #[test] + fn named_forward() { + #[derive(Borrow)] + #[borrow(forward)] + struct NamedForward { + value: String, + } + + let item = NamedForward { + value: "test".to_owned(), + }; + let borrowed: &str = item.borrow(); + + assert!(ptr::eq::(borrowed, item.value.borrow())); + } + + #[test] + fn field_forward() { + #[derive(Borrow)] + struct FieldForward(#[borrow(forward)] String); + + let item = FieldForward("test".to_owned()); + let borrowed: &str = item.borrow(); + + assert!(ptr::eq::(borrowed, item.0.borrow())); + } + + #[test] + fn named_field_forward() { + #[derive(Borrow)] + struct NamedFieldForward { + #[borrow(forward)] + value: String, + } + + let item = NamedFieldForward { + value: "test".to_owned(), + }; + let borrowed: &str = item.borrow(); + + assert!(ptr::eq::(borrowed, item.value.borrow())); + } + + #[test] + fn forward_generic_container() { + #[derive(Borrow)] + #[borrow(forward)] + struct Forward(Vec); + + let item = Forward(vec![1, 2, 3]); + let borrowed: &[i32] = item.borrow(); + + assert!(ptr::eq::<[i32]>(borrowed, item.0.as_slice())); + } + + #[test] + fn forward_with_internal_generic_name_collision() { + #[derive(Borrow)] + #[borrow(forward)] + struct Forward<__BorrowT>(Vec<__BorrowT>); + + let item = Forward(vec![1, 2, 3]); + let borrowed: &[i32] = item.borrow(); + + assert!(ptr::eq::<[i32]>(borrowed, item.0.as_slice())); + } + + #[cfg(nightly)] + mod never { + use super::*; + + #[derive(Borrow)] + struct Nothing(!); + } + + mod deprecated { + use super::*; + + #[derive(Borrow)] + #[deprecated(note = "struct")] + struct Deprecated(#[deprecated(note = "field")] i32); + + #[derive(Borrow)] + #[deprecated(note = "struct")] + struct DeprecatedNamed { + #[deprecated(note = "field")] + field: i32, + } + } + + mod trait_object { + use super::*; + + use core::fmt::Debug; + + #[derive(Borrow)] + struct DynDebug(dyn Debug); + + #[derive(Borrow)] + struct DynDebugSend(dyn Debug + Send); + + #[derive(Borrow)] + struct DynDebugLifetime<'a>(dyn Debug + 'a); + + #[test] + fn direct() { + fn assert_debug>() {} + fn assert_debug_send>() { + } + fn assert_debug_lifetime< + 'a, + T: ?Sized + core::borrow::Borrow, + >() { + } + + assert_debug::(); + assert_debug_send::(); + assert_debug_lifetime::>(); + } + } +} diff --git a/tests/borrow_mut.rs b/tests/borrow_mut.rs new file mode 100644 index 00000000..ce451e13 --- /dev/null +++ b/tests/borrow_mut.rs @@ -0,0 +1,307 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(nightly, feature(never_type))] +#![allow(dead_code)] // some code is tested for type checking only + +#[cfg(not(feature = "std"))] +extern crate alloc; + +#[cfg(not(feature = "std"))] +use alloc::{borrow::ToOwned, string::String, vec, vec::Vec}; + +#[cfg(feature = "std")] +use std::borrow::ToOwned; + +use core::{borrow::BorrowMut as _, ptr}; + +use derive_more::{Borrow, BorrowMut}; + +mod single_field { + use super::*; + + #[test] + fn tuple() { + #[derive(Borrow, BorrowMut)] + struct Tuple(String); + + let mut item = Tuple("test".to_owned()); + let field = ptr::addr_of_mut!(item.0); + let borrowed: &mut String = item.borrow_mut(); + + assert!(ptr::eq(borrowed, field)); + borrowed.push('!'); + assert_eq!(item.0, "test!"); + } + + #[test] + fn named() { + #[derive(Borrow, BorrowMut)] + struct Named { + value: String, + } + + let mut item = Named { + value: "test".to_owned(), + }; + let field = ptr::addr_of_mut!(item.value); + let borrowed: &mut String = item.borrow_mut(); + + assert!(ptr::eq(borrowed, field)); + borrowed.push('!'); + assert_eq!(item.value, "test!"); + } + + #[test] + fn generic() { + #[derive(Borrow, BorrowMut)] + struct Generic(T); + + let mut item = Generic("test".to_owned()); + let field = ptr::addr_of_mut!(item.0); + let borrowed: &mut String = item.borrow_mut(); + + assert!(ptr::eq(borrowed, field)); + borrowed.push('!'); + assert_eq!(item.0, "test!"); + } + + #[test] + fn field() { + #[derive(Borrow, BorrowMut)] + struct Field(#[borrow_mut] String); + + let mut item = Field("test".to_owned()); + let field = ptr::addr_of_mut!(item.0); + let borrowed: &mut String = item.borrow_mut(); + + assert!(ptr::eq(borrowed, field)); + borrowed.push('!'); + assert_eq!(item.0, "test!"); + } + + #[test] + fn named_field() { + #[derive(Borrow, BorrowMut)] + struct NamedField { + #[borrow_mut] + value: String, + } + + let mut item = NamedField { + value: "test".to_owned(), + }; + let field = ptr::addr_of_mut!(item.value); + let borrowed: &mut String = item.borrow_mut(); + + assert!(ptr::eq(borrowed, field)); + borrowed.push('!'); + assert_eq!(item.value, "test!"); + } + + #[test] + fn lifetime() { + #[derive(Borrow, BorrowMut)] + struct Lifetime<'a>(&'a mut i32); + + let mut value = 1; + let mut item = Lifetime(&mut value); + let field = ptr::addr_of_mut!(item.0); + let borrowed: &mut &mut i32 = item.borrow_mut(); + + assert!(ptr::eq(borrowed, field)); + **borrowed = 2; + assert_eq!(value, 2); + } + + #[test] + fn const_param() { + #[derive(Borrow, BorrowMut)] + struct ConstParam([i32; N]); + + let mut item = ConstParam([1, 2]); + let field = ptr::addr_of_mut!(item.0); + let borrowed: &mut [i32; 2] = item.borrow_mut(); + + assert!(ptr::eq(borrowed, field)); + borrowed[0] = 3; + assert_eq!(item.0, [3, 2]); + } + + #[test] + fn forward() { + #[derive(Borrow, BorrowMut)] + #[borrow(forward)] + #[borrow_mut(forward)] + struct Forward(String); + + let mut item = Forward("test".to_owned()); + let field = item.0.as_mut_str() as *mut str; + let borrowed: &mut str = item.borrow_mut(); + + assert!(ptr::eq::(borrowed, field)); + borrowed.make_ascii_uppercase(); + assert_eq!(item.0, "TEST"); + } + + #[test] + fn named_forward() { + #[derive(Borrow, BorrowMut)] + #[borrow(forward)] + #[borrow_mut(forward)] + struct NamedForward { + value: String, + } + + let mut item = NamedForward { + value: "test".to_owned(), + }; + let field = item.value.as_mut_str() as *mut str; + let borrowed: &mut str = item.borrow_mut(); + + assert!(ptr::eq::(borrowed, field)); + borrowed.make_ascii_uppercase(); + assert_eq!(item.value, "TEST"); + } + + #[test] + fn field_forward() { + #[derive(Borrow, BorrowMut)] + struct FieldForward( + #[borrow(forward)] + #[borrow_mut(forward)] + String, + ); + + let mut item = FieldForward("test".to_owned()); + let field = item.0.as_mut_str() as *mut str; + let borrowed: &mut str = item.borrow_mut(); + + assert!(ptr::eq::(borrowed, field)); + borrowed.make_ascii_uppercase(); + assert_eq!(item.0, "TEST"); + } + + #[test] + fn named_field_forward() { + #[derive(Borrow, BorrowMut)] + struct NamedFieldForward { + #[borrow(forward)] + #[borrow_mut(forward)] + value: String, + } + + let mut item = NamedFieldForward { + value: "test".to_owned(), + }; + let field = item.value.as_mut_str() as *mut str; + let borrowed: &mut str = item.borrow_mut(); + + assert!(ptr::eq::(borrowed, field)); + borrowed.make_ascii_uppercase(); + assert_eq!(item.value, "TEST"); + } + + #[test] + fn forward_generic_container() { + #[derive(Borrow, BorrowMut)] + #[borrow(forward)] + #[borrow_mut(forward)] + struct Forward(Vec); + + let mut item = Forward(vec![1, 2, 3]); + let field = item.0.as_mut_slice() as *mut [i32]; + let borrowed: &mut [i32] = item.borrow_mut(); + + assert!(ptr::eq::<[i32]>(borrowed, field)); + borrowed[0] = 4; + assert_eq!(item.0.as_slice(), &[4, 2, 3]); + } + + #[test] + fn forward_with_internal_generic_name_collision() { + #[derive(Borrow, BorrowMut)] + #[borrow(forward)] + #[borrow_mut(forward)] + struct Forward<__BorrowMutT>(Vec<__BorrowMutT>); + + let mut item = Forward(vec![1, 2, 3]); + let field = item.0.as_mut_slice() as *mut [i32]; + let borrowed: &mut [i32] = item.borrow_mut(); + + assert!(ptr::eq::<[i32]>(borrowed, field)); + borrowed[0] = 4; + assert_eq!(item.0.as_slice(), &[4, 2, 3]); + } + + #[cfg(nightly)] + mod never { + use super::*; + + #[derive(Borrow, BorrowMut)] + struct Nothing(!); + } + + mod deprecated { + use super::*; + + #[derive(Borrow, BorrowMut)] + #[deprecated(note = "struct")] + struct Deprecated(#[deprecated(note = "field")] i32); + + #[derive(Borrow, BorrowMut)] + #[deprecated(note = "struct")] + struct DeprecatedNamed { + #[deprecated(note = "field")] + field: i32, + } + } + + mod trait_object { + use super::*; + + use core::fmt::Debug; + + #[derive(Borrow, BorrowMut)] + struct DynDebug(dyn Debug); + + #[derive(Borrow, BorrowMut)] + struct DynDebugSend(dyn Debug + Send); + + #[derive(Borrow, BorrowMut)] + struct DynDebugLifetime<'a>(dyn Debug + 'a); + + #[test] + fn direct() { + fn assert_debug>() {} + fn assert_debug_send< + T: ?Sized + core::borrow::BorrowMut, + >() { + } + fn assert_debug_lifetime< + 'a, + T: ?Sized + core::borrow::BorrowMut, + >() { + } + + assert_debug::(); + assert_debug_send::(); + assert_debug_lifetime::>(); + } + } +} + +mod with_trait { + use super::*; + use derive_more::with_trait::{Borrow, BorrowMut}; + + #[derive(Borrow, BorrowMut)] + struct Tuple(String); + + #[test] + fn reexports_trait_and_derive() { + let mut item = Tuple("test".to_owned()); + let borrowed: &mut String = BorrowMut::borrow_mut(&mut item); + + borrowed.push('!'); + assert_eq!(item.0, "test!"); + } +} diff --git a/tests/compile_fail/borrow/associated_type.rs b/tests/compile_fail/borrow/associated_type.rs new file mode 100644 index 00000000..07de657e --- /dev/null +++ b/tests/compile_fail/borrow/associated_type.rs @@ -0,0 +1,8 @@ +trait Trait { + type Assoc; +} + +#[derive(derive_more::Borrow)] +struct Foo(T::Assoc); + +fn main() {} diff --git a/tests/compile_fail/borrow/associated_type.stderr b/tests/compile_fail/borrow/associated_type.stderr new file mode 100644 index 00000000..3eb2a5fc --- /dev/null +++ b/tests/compile_fail/borrow/associated_type.stderr @@ -0,0 +1,5 @@ +error: `Borrow` cannot be derived for an associated type projection involving generic parameters because the impl can overlap with `core`'s blanket `Borrow` implementation + --> tests/compile_fail/borrow/associated_type.rs:6:22 + | +6 | struct Foo(T::Assoc); + | ^ diff --git a/tests/compile_fail/borrow/const_projection.rs b/tests/compile_fail/borrow/const_projection.rs new file mode 100644 index 00000000..c9c3219c --- /dev/null +++ b/tests/compile_fail/borrow/const_projection.rs @@ -0,0 +1,10 @@ +trait Trait { + type Assoc; +} + +#[derive(derive_more::Borrow)] +struct Foo(<[i32; N] as Trait>::Assoc) +where + [i32; N]: Trait; + +fn main() {} diff --git a/tests/compile_fail/borrow/const_projection.stderr b/tests/compile_fail/borrow/const_projection.stderr new file mode 100644 index 00000000..d6d521be --- /dev/null +++ b/tests/compile_fail/borrow/const_projection.stderr @@ -0,0 +1,5 @@ +error: `Borrow` cannot be derived for an associated type projection involving generic parameters because the impl can overlap with `core`'s blanket `Borrow` implementation + --> tests/compile_fail/borrow/const_projection.rs:6:28 + | +6 | struct Foo(<[i32; N] as Trait>::Assoc) + | ^ diff --git a/tests/compile_fail/borrow/enum.rs b/tests/compile_fail/borrow/enum.rs new file mode 100644 index 00000000..0f864c7d --- /dev/null +++ b/tests/compile_fail/borrow/enum.rs @@ -0,0 +1,6 @@ +#[derive(derive_more::Borrow)] +enum Foo { + Bar(String), +} + +fn main() {} diff --git a/tests/compile_fail/borrow/enum.stderr b/tests/compile_fail/borrow/enum.stderr new file mode 100644 index 00000000..4c01f02f --- /dev/null +++ b/tests/compile_fail/borrow/enum.stderr @@ -0,0 +1,5 @@ +error: `Borrow` cannot be derived for enums + --> tests/compile_fail/borrow/enum.rs:2:1 + | +2 | enum Foo { + | ^^^^ diff --git a/tests/compile_fail/borrow/forward_box_associated_type.rs b/tests/compile_fail/borrow/forward_box_associated_type.rs new file mode 100644 index 00000000..c85b42c5 --- /dev/null +++ b/tests/compile_fail/borrow/forward_box_associated_type.rs @@ -0,0 +1,9 @@ +trait Trait { + type Assoc; +} + +#[derive(derive_more::Borrow)] +#[borrow(forward)] +struct Foo(Box); + +fn main() {} diff --git a/tests/compile_fail/borrow/forward_box_associated_type.stderr b/tests/compile_fail/borrow/forward_box_associated_type.stderr new file mode 100644 index 00000000..40bf6d6a --- /dev/null +++ b/tests/compile_fail/borrow/forward_box_associated_type.stderr @@ -0,0 +1,5 @@ +error: `#[borrow(forward)]` cannot be used on a generic parameter field, an associated type projection involving generic parameters, or a forwarding pointer to one because the impl can overlap with `core`'s blanket `Borrow` implementation + --> tests/compile_fail/borrow/forward_box_associated_type.rs:7:22 + | +7 | struct Foo(Box); + | ^^^ diff --git a/tests/compile_fail/borrow/forward_box_generic.rs b/tests/compile_fail/borrow/forward_box_generic.rs new file mode 100644 index 00000000..e48b2bca --- /dev/null +++ b/tests/compile_fail/borrow/forward_box_generic.rs @@ -0,0 +1,5 @@ +#[derive(derive_more::Borrow)] +#[borrow(forward)] +struct Foo(Box); + +fn main() {} diff --git a/tests/compile_fail/borrow/forward_box_generic.stderr b/tests/compile_fail/borrow/forward_box_generic.stderr new file mode 100644 index 00000000..19abbf2d --- /dev/null +++ b/tests/compile_fail/borrow/forward_box_generic.stderr @@ -0,0 +1,5 @@ +error: `#[borrow(forward)]` cannot be used on a generic parameter field, an associated type projection involving generic parameters, or a forwarding pointer to one because the impl can overlap with `core`'s blanket `Borrow` implementation + --> tests/compile_fail/borrow/forward_box_generic.rs:3:15 + | +3 | struct Foo(Box); + | ^^^ diff --git a/tests/compile_fail/borrow/forward_generic.rs b/tests/compile_fail/borrow/forward_generic.rs new file mode 100644 index 00000000..0e5a1355 --- /dev/null +++ b/tests/compile_fail/borrow/forward_generic.rs @@ -0,0 +1,5 @@ +#[derive(derive_more::Borrow)] +#[borrow(forward)] +struct Foo(T); + +fn main() {} diff --git a/tests/compile_fail/borrow/forward_generic.stderr b/tests/compile_fail/borrow/forward_generic.stderr new file mode 100644 index 00000000..a69761a5 --- /dev/null +++ b/tests/compile_fail/borrow/forward_generic.stderr @@ -0,0 +1,5 @@ +error: `#[borrow(forward)]` cannot be used on a generic parameter field, an associated type projection involving generic parameters, or a forwarding pointer to one because the impl can overlap with `core`'s blanket `Borrow` implementation + --> tests/compile_fail/borrow/forward_generic.rs:3:15 + | +3 | struct Foo(T); + | ^ diff --git a/tests/compile_fail/borrow/forward_ref_associated_type.rs b/tests/compile_fail/borrow/forward_ref_associated_type.rs new file mode 100644 index 00000000..9ac8ea74 --- /dev/null +++ b/tests/compile_fail/borrow/forward_ref_associated_type.rs @@ -0,0 +1,9 @@ +trait Trait { + type Assoc; +} + +#[derive(derive_more::Borrow)] +#[borrow(forward)] +struct Foo<'a, T: Trait>(&'a T::Assoc); + +fn main() {} diff --git a/tests/compile_fail/borrow/forward_ref_associated_type.stderr b/tests/compile_fail/borrow/forward_ref_associated_type.stderr new file mode 100644 index 00000000..b88467db --- /dev/null +++ b/tests/compile_fail/borrow/forward_ref_associated_type.stderr @@ -0,0 +1,5 @@ +error: `#[borrow(forward)]` cannot be used on a generic parameter field, an associated type projection involving generic parameters, or a forwarding pointer to one because the impl can overlap with `core`'s blanket `Borrow` implementation + --> tests/compile_fail/borrow/forward_ref_associated_type.rs:7:26 + | +7 | struct Foo<'a, T: Trait>(&'a T::Assoc); + | ^ diff --git a/tests/compile_fail/borrow/forward_ref_generic.rs b/tests/compile_fail/borrow/forward_ref_generic.rs new file mode 100644 index 00000000..c5287fd5 --- /dev/null +++ b/tests/compile_fail/borrow/forward_ref_generic.rs @@ -0,0 +1,5 @@ +#[derive(derive_more::Borrow)] +#[borrow(forward)] +struct Foo<'a, T>(&'a T); + +fn main() {} diff --git a/tests/compile_fail/borrow/forward_ref_generic.stderr b/tests/compile_fail/borrow/forward_ref_generic.stderr new file mode 100644 index 00000000..d04ef685 --- /dev/null +++ b/tests/compile_fail/borrow/forward_ref_generic.stderr @@ -0,0 +1,5 @@ +error: `#[borrow(forward)]` cannot be used on a generic parameter field, an associated type projection involving generic parameters, or a forwarding pointer to one because the impl can overlap with `core`'s blanket `Borrow` implementation + --> tests/compile_fail/borrow/forward_ref_generic.rs:3:19 + | +3 | struct Foo<'a, T>(&'a T); + | ^ diff --git a/tests/compile_fail/borrow/generic_projection.rs b/tests/compile_fail/borrow/generic_projection.rs new file mode 100644 index 00000000..23e86ab2 --- /dev/null +++ b/tests/compile_fail/borrow/generic_projection.rs @@ -0,0 +1,10 @@ +trait Trait { + type Assoc; +} + +#[derive(derive_more::Borrow)] +struct Foo( as Trait>::Assoc) +where + Vec: Trait; + +fn main() {} diff --git a/tests/compile_fail/borrow/generic_projection.stderr b/tests/compile_fail/borrow/generic_projection.stderr new file mode 100644 index 00000000..c8a22b2a --- /dev/null +++ b/tests/compile_fail/borrow/generic_projection.stderr @@ -0,0 +1,5 @@ +error: `Borrow` cannot be derived for an associated type projection involving generic parameters because the impl can overlap with `core`'s blanket `Borrow` implementation + --> tests/compile_fail/borrow/generic_projection.rs:6:15 + | +6 | struct Foo( as Trait>::Assoc) + | ^ diff --git a/tests/compile_fail/borrow/lifetime_projection.rs b/tests/compile_fail/borrow/lifetime_projection.rs new file mode 100644 index 00000000..78fba150 --- /dev/null +++ b/tests/compile_fail/borrow/lifetime_projection.rs @@ -0,0 +1,10 @@ +trait Trait { + type Assoc; +} + +#[derive(derive_more::Borrow)] +struct Foo<'a>(<&'a i32 as Trait>::Assoc) +where + &'a i32: Trait; + +fn main() {} diff --git a/tests/compile_fail/borrow/lifetime_projection.stderr b/tests/compile_fail/borrow/lifetime_projection.stderr new file mode 100644 index 00000000..93a5f144 --- /dev/null +++ b/tests/compile_fail/borrow/lifetime_projection.stderr @@ -0,0 +1,5 @@ +error: `Borrow` cannot be derived for an associated type projection involving generic parameters because the impl can overlap with `core`'s blanket `Borrow` implementation + --> tests/compile_fail/borrow/lifetime_projection.rs:6:16 + | +6 | struct Foo<'a>(<&'a i32 as Trait>::Assoc) + | ^ diff --git a/tests/compile_fail/borrow/multi_field.rs b/tests/compile_fail/borrow/multi_field.rs new file mode 100644 index 00000000..c0a44daf --- /dev/null +++ b/tests/compile_fail/borrow/multi_field.rs @@ -0,0 +1,7 @@ +#[derive(derive_more::Borrow)] +struct Foo { + bar: i32, + baz: f32, +} + +fn main() {} diff --git a/tests/compile_fail/borrow/multi_field.stderr b/tests/compile_fail/borrow/multi_field.stderr new file mode 100644 index 00000000..d5141bb2 --- /dev/null +++ b/tests/compile_fail/borrow/multi_field.stderr @@ -0,0 +1,9 @@ +error: `Borrow` can only be derived for structs with exactly one field + --> tests/compile_fail/borrow/multi_field.rs:2:12 + | +2 | struct Foo { + | ____________^ +3 | | bar: i32, +4 | | baz: f32, +5 | | } + | |_^ diff --git a/tests/compile_fail/borrow/multiple_field_attrs.rs b/tests/compile_fail/borrow/multiple_field_attrs.rs new file mode 100644 index 00000000..565e5288 --- /dev/null +++ b/tests/compile_fail/borrow/multiple_field_attrs.rs @@ -0,0 +1,8 @@ +#[derive(derive_more::Borrow)] +struct Foo( + #[borrow] + #[borrow(forward)] + String, +); + +fn main() {} diff --git a/tests/compile_fail/borrow/multiple_field_attrs.stderr b/tests/compile_fail/borrow/multiple_field_attrs.stderr new file mode 100644 index 00000000..11f64244 --- /dev/null +++ b/tests/compile_fail/borrow/multiple_field_attrs.stderr @@ -0,0 +1,5 @@ +error: only single kind of `#[borrow(...)]` attribute is allowed here + --> tests/compile_fail/borrow/multiple_field_attrs.rs:4:5 + | +4 | #[borrow(forward)] + | ^ diff --git a/tests/compile_fail/borrow/multiple_struct_attrs.rs b/tests/compile_fail/borrow/multiple_struct_attrs.rs new file mode 100644 index 00000000..05f18043 --- /dev/null +++ b/tests/compile_fail/borrow/multiple_struct_attrs.rs @@ -0,0 +1,6 @@ +#[derive(derive_more::Borrow)] +#[borrow(forward)] +#[borrow(forward)] +struct Foo(String); + +fn main() {} diff --git a/tests/compile_fail/borrow/multiple_struct_attrs.stderr b/tests/compile_fail/borrow/multiple_struct_attrs.stderr new file mode 100644 index 00000000..2c717dd4 --- /dev/null +++ b/tests/compile_fail/borrow/multiple_struct_attrs.stderr @@ -0,0 +1,5 @@ +error: only single `#[borrow(...)]` attribute is allowed here + --> tests/compile_fail/borrow/multiple_struct_attrs.rs:3:1 + | +3 | #[borrow(forward)] + | ^ diff --git a/tests/compile_fail/borrow/non_borrowable_forward.rs b/tests/compile_fail/borrow/non_borrowable_forward.rs new file mode 100644 index 00000000..8dd80db4 --- /dev/null +++ b/tests/compile_fail/borrow/non_borrowable_forward.rs @@ -0,0 +1,10 @@ +#[derive(derive_more::Borrow)] +#[borrow(forward)] +struct Foo(i32); + +fn main() { + use core::borrow::Borrow as _; + + let item = Foo(1); + let _: &str = item.borrow(); +} diff --git a/tests/compile_fail/borrow/non_borrowable_forward.stderr b/tests/compile_fail/borrow/non_borrowable_forward.stderr new file mode 100644 index 00000000..08540bf3 --- /dev/null +++ b/tests/compile_fail/borrow/non_borrowable_forward.stderr @@ -0,0 +1,23 @@ +error[E0277]: the trait bound `i32: Borrow` is not satisfied + --> tests/compile_fail/borrow/non_borrowable_forward.rs:9:24 + | +9 | let _: &str = item.borrow(); + | ^^^^^^ the trait `Borrow` is not implemented for `i32` + | +help: the trait `Borrow` is not implemented for `Foo` + but trait `Borrow` is implemented for it + --> tests/compile_fail/borrow/non_borrowable_forward.rs:1:10 + | +1 | #[derive(derive_more::Borrow)] + | ^^^^^^^^^^^^^^^^^^^ + = help: for that trait implementation, expected `i32`, found `str` +note: required for `Foo` to implement `Borrow` + --> tests/compile_fail/borrow/non_borrowable_forward.rs:3:8 + | +1 | #[derive(derive_more::Borrow)] + | ------------------- type parameter would need to implement `Borrow` +2 | #[borrow(forward)] +3 | struct Foo(i32); + | ^^^ + = help: consider manually implementing `Borrow` to avoid undesired bounds + = note: this error originates in the derive macro `derive_more::Borrow` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/compile_fail/borrow/parenthesized_associated_type.rs b/tests/compile_fail/borrow/parenthesized_associated_type.rs new file mode 100644 index 00000000..d4b4bfe5 --- /dev/null +++ b/tests/compile_fail/borrow/parenthesized_associated_type.rs @@ -0,0 +1,10 @@ +#![allow(unused_parens)] + +trait Trait { + type Assoc; +} + +#[derive(derive_more::Borrow)] +struct Foo((T::Assoc)); + +fn main() {} diff --git a/tests/compile_fail/borrow/parenthesized_associated_type.stderr b/tests/compile_fail/borrow/parenthesized_associated_type.stderr new file mode 100644 index 00000000..414ad6a3 --- /dev/null +++ b/tests/compile_fail/borrow/parenthesized_associated_type.stderr @@ -0,0 +1,5 @@ +error: `Borrow` cannot be derived for an associated type projection involving generic parameters because the impl can overlap with `core`'s blanket `Borrow` implementation + --> tests/compile_fail/borrow/parenthesized_associated_type.rs:8:22 + | +8 | struct Foo((T::Assoc)); + | ^^^^^^^^^^ diff --git a/tests/compile_fail/borrow/qualified_associated_type.rs b/tests/compile_fail/borrow/qualified_associated_type.rs new file mode 100644 index 00000000..29067715 --- /dev/null +++ b/tests/compile_fail/borrow/qualified_associated_type.rs @@ -0,0 +1,8 @@ +trait Trait { + type Assoc; +} + +#[derive(derive_more::Borrow)] +struct Foo(::Assoc); + +fn main() {} diff --git a/tests/compile_fail/borrow/qualified_associated_type.stderr b/tests/compile_fail/borrow/qualified_associated_type.stderr new file mode 100644 index 00000000..f4bd6320 --- /dev/null +++ b/tests/compile_fail/borrow/qualified_associated_type.stderr @@ -0,0 +1,5 @@ +error: `Borrow` cannot be derived for an associated type projection involving generic parameters because the impl can overlap with `core`'s blanket `Borrow` implementation + --> tests/compile_fail/borrow/qualified_associated_type.rs:6:22 + | +6 | struct Foo(::Assoc); + | ^ diff --git a/tests/compile_fail/borrow/skip.rs b/tests/compile_fail/borrow/skip.rs new file mode 100644 index 00000000..f696cc20 --- /dev/null +++ b/tests/compile_fail/borrow/skip.rs @@ -0,0 +1,4 @@ +#[derive(derive_more::Borrow)] +struct Foo(#[borrow(skip)] i32); + +fn main() {} diff --git a/tests/compile_fail/borrow/skip.stderr b/tests/compile_fail/borrow/skip.stderr new file mode 100644 index 00000000..479a96ef --- /dev/null +++ b/tests/compile_fail/borrow/skip.stderr @@ -0,0 +1,5 @@ +error: `#[borrow(skip)]` cannot be used when deriving `Borrow` because exactly one field must be borrowed + --> tests/compile_fail/borrow/skip.rs:2:12 + | +2 | struct Foo(#[borrow(skip)] i32); + | ^ diff --git a/tests/compile_fail/borrow/skip_and_others.rs b/tests/compile_fail/borrow/skip_and_others.rs new file mode 100644 index 00000000..59f2fb56 --- /dev/null +++ b/tests/compile_fail/borrow/skip_and_others.rs @@ -0,0 +1,8 @@ +#[derive(derive_more::Borrow)] +struct Foo( + #[borrow] + #[borrow(skip)] + i32, +); + +fn main() {} diff --git a/tests/compile_fail/borrow/skip_and_others.stderr b/tests/compile_fail/borrow/skip_and_others.stderr new file mode 100644 index 00000000..8142df5b --- /dev/null +++ b/tests/compile_fail/borrow/skip_and_others.stderr @@ -0,0 +1,5 @@ +error: only single kind of `#[borrow(...)]` attribute is allowed here + --> tests/compile_fail/borrow/skip_and_others.rs:4:5 + | +4 | #[borrow(skip)] + | ^ diff --git a/tests/compile_fail/borrow/struct_and_field.rs b/tests/compile_fail/borrow/struct_and_field.rs new file mode 100644 index 00000000..89b4c0c7 --- /dev/null +++ b/tests/compile_fail/borrow/struct_and_field.rs @@ -0,0 +1,8 @@ +#[derive(derive_more::Borrow)] +#[borrow(forward)] +struct Foo { + #[borrow] + bar: i32, +} + +fn main() {} diff --git a/tests/compile_fail/borrow/struct_and_field.stderr b/tests/compile_fail/borrow/struct_and_field.stderr new file mode 100644 index 00000000..1da4a162 --- /dev/null +++ b/tests/compile_fail/borrow/struct_and_field.stderr @@ -0,0 +1,5 @@ +error: `#[borrow(...)]` cannot be placed on both struct and its field + --> tests/compile_fail/borrow/struct_and_field.rs:4:5 + | +4 | #[borrow] + | ^ diff --git a/tests/compile_fail/borrow/struct_attr_empty.rs b/tests/compile_fail/borrow/struct_attr_empty.rs new file mode 100644 index 00000000..c093d0fa --- /dev/null +++ b/tests/compile_fail/borrow/struct_attr_empty.rs @@ -0,0 +1,5 @@ +#[derive(derive_more::Borrow)] +#[borrow(forward)] +struct Foo; + +fn main() {} diff --git a/tests/compile_fail/borrow/struct_attr_empty.stderr b/tests/compile_fail/borrow/struct_attr_empty.stderr new file mode 100644 index 00000000..4cd557ad --- /dev/null +++ b/tests/compile_fail/borrow/struct_attr_empty.stderr @@ -0,0 +1,5 @@ +error: `Borrow` can only be derived for structs with exactly one field + --> tests/compile_fail/borrow/struct_attr_empty.rs:3:1 + | +3 | struct Foo; + | ^^^^^^ diff --git a/tests/compile_fail/borrow/struct_attr_multiple_fields.rs b/tests/compile_fail/borrow/struct_attr_multiple_fields.rs new file mode 100644 index 00000000..2e3f2857 --- /dev/null +++ b/tests/compile_fail/borrow/struct_attr_multiple_fields.rs @@ -0,0 +1,8 @@ +#[derive(derive_more::Borrow)] +#[borrow(forward)] +struct Foo { + bar: i32, + baz: f32, +} + +fn main() {} diff --git a/tests/compile_fail/borrow/struct_attr_multiple_fields.stderr b/tests/compile_fail/borrow/struct_attr_multiple_fields.stderr new file mode 100644 index 00000000..5278583f --- /dev/null +++ b/tests/compile_fail/borrow/struct_attr_multiple_fields.stderr @@ -0,0 +1,9 @@ +error: `Borrow` can only be derived for structs with exactly one field + --> tests/compile_fail/borrow/struct_attr_multiple_fields.rs:3:12 + | +3 | struct Foo { + | ____________^ +4 | | bar: i32, +5 | | baz: f32, +6 | | } + | |_^ diff --git a/tests/compile_fail/borrow/union.rs b/tests/compile_fail/borrow/union.rs new file mode 100644 index 00000000..d64527bd --- /dev/null +++ b/tests/compile_fail/borrow/union.rs @@ -0,0 +1,7 @@ +#[derive(derive_more::Borrow)] +union Foo { + bar: i32, + baz: f32, +} + +fn main() {} diff --git a/tests/compile_fail/borrow/union.stderr b/tests/compile_fail/borrow/union.stderr new file mode 100644 index 00000000..aa3c9aea --- /dev/null +++ b/tests/compile_fail/borrow/union.stderr @@ -0,0 +1,5 @@ +error: `Borrow` cannot be derived for unions + --> tests/compile_fail/borrow/union.rs:2:1 + | +2 | union Foo { + | ^^^^^ diff --git a/tests/compile_fail/borrow/unit.rs b/tests/compile_fail/borrow/unit.rs new file mode 100644 index 00000000..581e79f2 --- /dev/null +++ b/tests/compile_fail/borrow/unit.rs @@ -0,0 +1,4 @@ +#[derive(derive_more::Borrow)] +struct Foo; + +fn main() {} diff --git a/tests/compile_fail/borrow/unit.stderr b/tests/compile_fail/borrow/unit.stderr new file mode 100644 index 00000000..f9d6d895 --- /dev/null +++ b/tests/compile_fail/borrow/unit.stderr @@ -0,0 +1,5 @@ +error: `Borrow` can only be derived for structs with exactly one field + --> tests/compile_fail/borrow/unit.rs:2:1 + | +2 | struct Foo; + | ^^^^^^ diff --git a/tests/compile_fail/borrow/unknown_field_attr_arg.rs b/tests/compile_fail/borrow/unknown_field_attr_arg.rs new file mode 100644 index 00000000..54526e1c --- /dev/null +++ b/tests/compile_fail/borrow/unknown_field_attr_arg.rs @@ -0,0 +1,7 @@ +#[derive(derive_more::Borrow)] +struct Foo { + #[borrow(baz)] + bar: i32, +} + +fn main() {} diff --git a/tests/compile_fail/borrow/unknown_field_attr_arg.stderr b/tests/compile_fail/borrow/unknown_field_attr_arg.stderr new file mode 100644 index 00000000..7944a9e7 --- /dev/null +++ b/tests/compile_fail/borrow/unknown_field_attr_arg.stderr @@ -0,0 +1,5 @@ +error: expected one of: `forward`, `skip`, `ignore` + --> tests/compile_fail/borrow/unknown_field_attr_arg.rs:3:14 + | +3 | #[borrow(baz)] + | ^^^ diff --git a/tests/compile_fail/borrow/unknown_struct_attr_arg.rs b/tests/compile_fail/borrow/unknown_struct_attr_arg.rs new file mode 100644 index 00000000..effbac22 --- /dev/null +++ b/tests/compile_fail/borrow/unknown_struct_attr_arg.rs @@ -0,0 +1,7 @@ +#[derive(derive_more::Borrow)] +#[borrow(baz)] +struct Foo { + bar: i32, +} + +fn main() {} diff --git a/tests/compile_fail/borrow/unknown_struct_attr_arg.stderr b/tests/compile_fail/borrow/unknown_struct_attr_arg.stderr new file mode 100644 index 00000000..63a348a0 --- /dev/null +++ b/tests/compile_fail/borrow/unknown_struct_attr_arg.stderr @@ -0,0 +1,5 @@ +error: only `forward` allowed here + --> tests/compile_fail/borrow/unknown_struct_attr_arg.rs:2:10 + | +2 | #[borrow(baz)] + | ^^^ diff --git a/tests/compile_fail/borrow_mut/associated_type.rs b/tests/compile_fail/borrow_mut/associated_type.rs new file mode 100644 index 00000000..5473168b --- /dev/null +++ b/tests/compile_fail/borrow_mut/associated_type.rs @@ -0,0 +1,8 @@ +trait Trait { + type Assoc; +} + +#[derive(derive_more::BorrowMut)] +struct Foo(T::Assoc); + +fn main() {} diff --git a/tests/compile_fail/borrow_mut/associated_type.stderr b/tests/compile_fail/borrow_mut/associated_type.stderr new file mode 100644 index 00000000..127522a8 --- /dev/null +++ b/tests/compile_fail/borrow_mut/associated_type.stderr @@ -0,0 +1,5 @@ +error: `BorrowMut` cannot be derived for an associated type projection involving generic parameters because the impl can overlap with `core`'s blanket `BorrowMut` implementation + --> tests/compile_fail/borrow_mut/associated_type.rs:6:22 + | +6 | struct Foo(T::Assoc); + | ^ diff --git a/tests/compile_fail/borrow_mut/enum.rs b/tests/compile_fail/borrow_mut/enum.rs new file mode 100644 index 00000000..4fa52457 --- /dev/null +++ b/tests/compile_fail/borrow_mut/enum.rs @@ -0,0 +1,6 @@ +#[derive(derive_more::BorrowMut)] +enum Foo { + Bar(i32), +} + +fn main() {} diff --git a/tests/compile_fail/borrow_mut/enum.stderr b/tests/compile_fail/borrow_mut/enum.stderr new file mode 100644 index 00000000..8d0aed08 --- /dev/null +++ b/tests/compile_fail/borrow_mut/enum.stderr @@ -0,0 +1,5 @@ +error: `BorrowMut` cannot be derived for enums + --> tests/compile_fail/borrow_mut/enum.rs:2:1 + | +2 | enum Foo { + | ^^^^ diff --git a/tests/compile_fail/borrow_mut/forward_generic.rs b/tests/compile_fail/borrow_mut/forward_generic.rs new file mode 100644 index 00000000..de9cc257 --- /dev/null +++ b/tests/compile_fail/borrow_mut/forward_generic.rs @@ -0,0 +1,5 @@ +#[derive(derive_more::BorrowMut)] +#[borrow_mut(forward)] +struct Foo(T); + +fn main() {} diff --git a/tests/compile_fail/borrow_mut/forward_generic.stderr b/tests/compile_fail/borrow_mut/forward_generic.stderr new file mode 100644 index 00000000..e3d04e32 --- /dev/null +++ b/tests/compile_fail/borrow_mut/forward_generic.stderr @@ -0,0 +1,5 @@ +error: `#[borrow_mut(forward)]` cannot be used on a generic parameter field, an associated type projection involving generic parameters, or a forwarding pointer to one because the impl can overlap with `core`'s blanket `BorrowMut` implementation + --> tests/compile_fail/borrow_mut/forward_generic.rs:3:15 + | +3 | struct Foo(T); + | ^ diff --git a/tests/compile_fail/borrow_mut/multi_field.rs b/tests/compile_fail/borrow_mut/multi_field.rs new file mode 100644 index 00000000..0aec77c7 --- /dev/null +++ b/tests/compile_fail/borrow_mut/multi_field.rs @@ -0,0 +1,4 @@ +#[derive(derive_more::BorrowMut)] +struct Foo(i32, i32); + +fn main() {} diff --git a/tests/compile_fail/borrow_mut/multi_field.stderr b/tests/compile_fail/borrow_mut/multi_field.stderr new file mode 100644 index 00000000..c0e4db55 --- /dev/null +++ b/tests/compile_fail/borrow_mut/multi_field.stderr @@ -0,0 +1,5 @@ +error: `BorrowMut` can only be derived for structs with exactly one field + --> tests/compile_fail/borrow_mut/multi_field.rs:2:11 + | +2 | struct Foo(i32, i32); + | ^^^^^^^^^^ diff --git a/tests/compile_fail/borrow_mut/non_borrowable_forward.rs b/tests/compile_fail/borrow_mut/non_borrowable_forward.rs new file mode 100644 index 00000000..1f0c25d3 --- /dev/null +++ b/tests/compile_fail/borrow_mut/non_borrowable_forward.rs @@ -0,0 +1,11 @@ +#[derive(derive_more::Borrow, derive_more::BorrowMut)] +#[borrow(forward)] +#[borrow_mut(forward)] +struct Foo(i32); + +fn main() { + use core::borrow::BorrowMut as _; + + let mut item = Foo(1); + let _: &mut str = item.borrow_mut(); +} diff --git a/tests/compile_fail/borrow_mut/non_borrowable_forward.stderr b/tests/compile_fail/borrow_mut/non_borrowable_forward.stderr new file mode 100644 index 00000000..7067e99d --- /dev/null +++ b/tests/compile_fail/borrow_mut/non_borrowable_forward.stderr @@ -0,0 +1,23 @@ +error[E0277]: the trait bound `i32: BorrowMut` is not satisfied + --> tests/compile_fail/borrow_mut/non_borrowable_forward.rs:10:28 + | +10 | let _: &mut str = item.borrow_mut(); + | ^^^^^^^^^^ the trait `BorrowMut` is not implemented for `i32` + | +help: the trait `BorrowMut` is not implemented for `Foo` + but trait `BorrowMut` is implemented for it + --> tests/compile_fail/borrow_mut/non_borrowable_forward.rs:1:31 + | + 1 | #[derive(derive_more::Borrow, derive_more::BorrowMut)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = help: for that trait implementation, expected `i32`, found `str` +note: required for `Foo` to implement `BorrowMut` + --> tests/compile_fail/borrow_mut/non_borrowable_forward.rs:4:8 + | + 1 | #[derive(derive_more::Borrow, derive_more::BorrowMut)] + | ---------------------- type parameter would need to implement `BorrowMut` +... + 4 | struct Foo(i32); + | ^^^ + = help: consider manually implementing `BorrowMut` to avoid undesired bounds + = note: this error originates in the derive macro `derive_more::BorrowMut` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/compile_fail/borrow_mut/skip.rs b/tests/compile_fail/borrow_mut/skip.rs new file mode 100644 index 00000000..350a008b --- /dev/null +++ b/tests/compile_fail/borrow_mut/skip.rs @@ -0,0 +1,4 @@ +#[derive(derive_more::BorrowMut)] +struct Foo(#[borrow_mut(skip)] i32); + +fn main() {} diff --git a/tests/compile_fail/borrow_mut/skip.stderr b/tests/compile_fail/borrow_mut/skip.stderr new file mode 100644 index 00000000..ad0205d7 --- /dev/null +++ b/tests/compile_fail/borrow_mut/skip.stderr @@ -0,0 +1,5 @@ +error: `#[borrow_mut(skip)]` cannot be used when deriving `BorrowMut` because exactly one field must be borrowed + --> tests/compile_fail/borrow_mut/skip.rs:2:12 + | +2 | struct Foo(#[borrow_mut(skip)] i32); + | ^ diff --git a/tests/compile_fail/borrow_mut/struct_and_field.rs b/tests/compile_fail/borrow_mut/struct_and_field.rs new file mode 100644 index 00000000..b4a123d8 --- /dev/null +++ b/tests/compile_fail/borrow_mut/struct_and_field.rs @@ -0,0 +1,8 @@ +#[derive(derive_more::BorrowMut)] +#[borrow_mut(forward)] +struct Foo( + #[borrow_mut] + i32, +); + +fn main() {} diff --git a/tests/compile_fail/borrow_mut/struct_and_field.stderr b/tests/compile_fail/borrow_mut/struct_and_field.stderr new file mode 100644 index 00000000..a147d60c --- /dev/null +++ b/tests/compile_fail/borrow_mut/struct_and_field.stderr @@ -0,0 +1,5 @@ +error: `#[borrow_mut(...)]` cannot be placed on both struct and its field + --> tests/compile_fail/borrow_mut/struct_and_field.rs:4:5 + | +4 | #[borrow_mut] + | ^ diff --git a/tests/compile_fail/borrow_mut/union.rs b/tests/compile_fail/borrow_mut/union.rs new file mode 100644 index 00000000..1c2365ce --- /dev/null +++ b/tests/compile_fail/borrow_mut/union.rs @@ -0,0 +1,6 @@ +#[derive(derive_more::BorrowMut)] +union Foo { + bar: i32, +} + +fn main() {} diff --git a/tests/compile_fail/borrow_mut/union.stderr b/tests/compile_fail/borrow_mut/union.stderr new file mode 100644 index 00000000..eb813f07 --- /dev/null +++ b/tests/compile_fail/borrow_mut/union.stderr @@ -0,0 +1,5 @@ +error: `BorrowMut` cannot be derived for unions + --> tests/compile_fail/borrow_mut/union.rs:2:1 + | +2 | union Foo { + | ^^^^^ diff --git a/tests/compile_fail/borrow_mut/unit.rs b/tests/compile_fail/borrow_mut/unit.rs new file mode 100644 index 00000000..96f02531 --- /dev/null +++ b/tests/compile_fail/borrow_mut/unit.rs @@ -0,0 +1,4 @@ +#[derive(derive_more::BorrowMut)] +struct Foo; + +fn main() {} diff --git a/tests/compile_fail/borrow_mut/unit.stderr b/tests/compile_fail/borrow_mut/unit.stderr new file mode 100644 index 00000000..fa3db961 --- /dev/null +++ b/tests/compile_fail/borrow_mut/unit.stderr @@ -0,0 +1,5 @@ +error: `BorrowMut` can only be derived for structs with exactly one field + --> tests/compile_fail/borrow_mut/unit.rs:2:1 + | +2 | struct Foo; + | ^^^^^^ diff --git a/tests/compile_fail/borrow_mut/unknown_field_attr_arg.rs b/tests/compile_fail/borrow_mut/unknown_field_attr_arg.rs new file mode 100644 index 00000000..05c8360a --- /dev/null +++ b/tests/compile_fail/borrow_mut/unknown_field_attr_arg.rs @@ -0,0 +1,4 @@ +#[derive(derive_more::BorrowMut)] +struct Foo(#[borrow_mut(baz)] i32); + +fn main() {} diff --git a/tests/compile_fail/borrow_mut/unknown_field_attr_arg.stderr b/tests/compile_fail/borrow_mut/unknown_field_attr_arg.stderr new file mode 100644 index 00000000..ffd88cbf --- /dev/null +++ b/tests/compile_fail/borrow_mut/unknown_field_attr_arg.stderr @@ -0,0 +1,5 @@ +error: expected one of: `forward`, `skip`, `ignore` + --> tests/compile_fail/borrow_mut/unknown_field_attr_arg.rs:2:25 + | +2 | struct Foo(#[borrow_mut(baz)] i32); + | ^^^ diff --git a/tests/compile_fail/borrow_mut/unknown_struct_attr_arg.rs b/tests/compile_fail/borrow_mut/unknown_struct_attr_arg.rs new file mode 100644 index 00000000..bec4e73c --- /dev/null +++ b/tests/compile_fail/borrow_mut/unknown_struct_attr_arg.rs @@ -0,0 +1,5 @@ +#[derive(derive_more::BorrowMut)] +#[borrow_mut(baz)] +struct Foo(i32); + +fn main() {} diff --git a/tests/compile_fail/borrow_mut/unknown_struct_attr_arg.stderr b/tests/compile_fail/borrow_mut/unknown_struct_attr_arg.stderr new file mode 100644 index 00000000..f93a5a11 --- /dev/null +++ b/tests/compile_fail/borrow_mut/unknown_struct_attr_arg.stderr @@ -0,0 +1,5 @@ +error: only `forward` allowed here + --> tests/compile_fail/borrow_mut/unknown_struct_attr_arg.rs:2:14 + | +2 | #[borrow_mut(baz)] + | ^^^ diff --git a/tests/compile_fail/eq/non_eq_field.stderr b/tests/compile_fail/eq/non_eq_field.stderr index ae02e14a..e93c539b 100644 --- a/tests/compile_fail/eq/non_eq_field.stderr +++ b/tests/compile_fail/eq/non_eq_field.stderr @@ -14,5 +14,4 @@ error[E0277]: the trait bound `f32: Eq` is not satisfied u128 u16 and $N others - = help: see issue #48214 = note: this error originates in the derive macro `derive_more::Eq` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/generics.rs b/tests/generics.rs index 42c568a4..1a33056c 100644 --- a/tests/generics.rs +++ b/tests/generics.rs @@ -2,8 +2,8 @@ #![allow(dead_code)] // some code is tested for type checking only use derive_more::{ - Add, AddAssign, Constructor, Deref, DerefMut, Display, Error, From, FromStr, Index, - IndexMut, IntoIterator, Mul, MulAssign, Not, Sum, TryInto, + Add, AddAssign, Borrow, BorrowMut, Constructor, Deref, DerefMut, Display, Error, + From, FromStr, Index, IndexMut, IntoIterator, Mul, MulAssign, Not, Sum, TryInto, }; #[derive( @@ -21,6 +21,8 @@ use derive_more::{ Deref, DerefMut, IntoIterator, + Borrow, + BorrowMut, Constructor )] #[deref(forward)] @@ -54,6 +56,8 @@ struct WrappedDouble2(T, U); Deref, DerefMut, IntoIterator, + Borrow, + BorrowMut, Constructor )] struct WrappedWithConst(T); @@ -72,6 +76,8 @@ struct WrappedWithConst(T); Deref, DerefMut, IntoIterator, + Borrow, + BorrowMut, Constructor, Sum )]