diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index dc64b8be..312d17f6 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -10,30 +10,16 @@ Related to - - - ## Synopsis - - - ## Solution - - - ## Checklist - [ ] Documentation is updated (if required) - [ ] Tests are added/updated (if required) -- [ ] [CHANGELOG entry][l:1] is added (if required) - - - - -[l:1]: /CHANGELOG.md +- [ ] [CHANGELOG entry](/CHANGELOG.md) is added (if required) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95d036f0..b0ba4f43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ([#300](https://github.com/JelteF/derive_more/pull/300)) - `#[inline]` attributes to `IsVariant` and `Debug` implementations. ([#334](https://github.com/JelteF/derive_more/pull/334) +- Add `#[track_caller]` to `Add`, `Mul`, `AddAssign` and `MulAssign` derives + ([#378](https://github.com/JelteF/derive_more/pull/378) + ### Changed @@ -84,8 +87,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). should prevent code style linters from attempting to modify the generated code. - Upgrade to `syn` 2.0. -- The `Error` derive now works in nightly `no_std` environments when enabling - `#![feature(error_in_core)]`. +- The `Error` derive now works in nightly `no_std` environments ### Fixed diff --git a/ci/test_all_features.sh b/ci/test_all_features.sh index 9357d570..ada63e50 100755 --- a/ci/test_all_features.sh +++ b/ci/test_all_features.sh @@ -8,5 +8,5 @@ fi set -euxo pipefail for feature in $(tomljson Cargo.toml | jq --raw-output '.features | keys[]' | grep -v 'default\|std\|full\|testing-helpers'); do - cargo +nightly test -p derive_more --tests --no-default-features --features "$feature$std,testing-helpers" + RUSTFLAGS='-D warnings' cargo +nightly test -p derive_more --tests --no-default-features --features "$feature$std,testing-helpers" done diff --git a/impl/Cargo.toml b/impl/Cargo.toml index dc6bcf53..7e69500d 100644 --- a/impl/Cargo.toml +++ b/impl/Cargo.toml @@ -44,7 +44,7 @@ features = ["full"] rustdoc-args = ["--cfg", "docsrs"] [lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ["cfg(ci)", "cfg(nighthly)"] } +unexpected_cfgs = { level = "warn", check-cfg = ["cfg(ci)", "cfg(nightly)"] } [features] default = [] diff --git a/impl/doc/error.md b/impl/doc/error.md index cc755cb2..2aace519 100644 --- a/impl/doc/error.md +++ b/impl/doc/error.md @@ -44,11 +44,9 @@ ignored for one of these methods by using `#[error(not(backtrace))]` or ### What works in `no_std`? -If you want to use the `Error` derive on `no_std` environments, then you need to -compile with nightly and enable this feature: -```ignore -#![feature(error_in_core)] -``` +If you want to use the `Error` derive on `no_std` environments, then +you need to compile with nightly, or wait until Rust 1.81 when `Error` +in `core` is expected to be stabilized. Backtraces don't work though, because the `Backtrace` type is only available in `std`. @@ -59,9 +57,9 @@ Backtraces don't work though, because the `Backtrace` type is only available in ## Example usage ```rust -# #![cfg_attr(nightly, feature(error_generic_member_access, error_in_core))] -// Nightly requires enabling these features: -// #![feature(error_generic_member_access, error_in_core)] +# #![cfg_attr(nightly, feature(error_generic_member_access))] +// Nightly requires enabling this feature: +// #![feature(error_generic_member_access)] # #[cfg(not(nightly))] fn main() {} # #[cfg(nightly)] fn main() { # use core::error::{request_ref, request_value, Error as __}; diff --git a/impl/src/add_assign_like.rs b/impl/src/add_assign_like.rs index 49510eb7..7d476732 100644 --- a/impl/src/add_assign_like.rs +++ b/impl/src/add_assign_like.rs @@ -31,6 +31,7 @@ pub fn expand(input: &DeriveInput, trait_name: &str) -> TokenStream { #[automatically_derived] impl #impl_generics derive_more::#trait_ident for #input_type #ty_generics #where_clause { #[inline] + #[track_caller] fn #method_ident(&mut self, rhs: #input_type #ty_generics) { #( #exprs; )* } diff --git a/impl/src/add_like.rs b/impl/src/add_like.rs index 095bf37f..097a89c6 100644 --- a/impl/src/add_like.rs +++ b/impl/src/add_like.rs @@ -46,6 +46,7 @@ pub fn expand(input: &DeriveInput, trait_name: &str) -> TokenStream { type Output = #output_type; #[inline] + #[track_caller] fn #method_ident(self, rhs: #input_type #ty_generics) -> #output_type { #block } diff --git a/impl/src/fmt/debug.rs b/impl/src/fmt/debug.rs index 68e873e1..524f0218 100644 --- a/impl/src/fmt/debug.rs +++ b/impl/src/fmt/debug.rs @@ -24,9 +24,21 @@ pub fn expand(input: &syn::DeriveInput, _: &str) -> syn::Result { .unwrap_or_default(); let ident = &input.ident; + let type_params: Vec<_> = input + .generics + .params + .iter() + .filter_map(|p| match p { + syn::GenericParam::Type(t) => Some(&t.ident), + syn::GenericParam::Const(..) | syn::GenericParam::Lifetime(..) => None, + }) + .collect(); + let (bounds, body) = match &input.data { - syn::Data::Struct(s) => expand_struct(attrs, ident, s, &attr_name), - syn::Data::Enum(e) => expand_enum(attrs, e, &attr_name), + syn::Data::Struct(s) => { + expand_struct(attrs, ident, s, &type_params, &attr_name) + } + syn::Data::Enum(e) => expand_enum(attrs, e, &type_params, &attr_name), syn::Data::Union(_) => { return Err(syn::Error::new( input.span(), @@ -64,11 +76,13 @@ fn expand_struct( attrs: ContainerAttributes, ident: &Ident, s: &syn::DataStruct, + type_params: &[&syn::Ident], attr_name: &syn::Ident, ) -> syn::Result<(Vec, TokenStream)> { let s = Expansion { attr: &attrs, fields: &s.fields, + type_params, ident, attr_name, }; @@ -82,7 +96,7 @@ fn expand_struct( .ident .clone() .map_or_else(|| syn::Member::Unnamed(i.into()), syn::Member::Named); - quote! { let #var = &&self.#member; } + quote! { let #var = &self.#member; } }); let body = quote! { @@ -99,6 +113,7 @@ fn expand_struct( fn expand_enum( mut attrs: ContainerAttributes, e: &syn::DataEnum, + type_params: &[&syn::Ident], attr_name: &syn::Ident, ) -> syn::Result<(Vec, TokenStream)> { if let Some(enum_fmt) = attrs.fmt.as_ref() { @@ -136,6 +151,7 @@ fn expand_enum( let v = Expansion { attr: &attrs, fields: &variant.fields, + type_params, ident, attr_name, }; @@ -195,6 +211,9 @@ struct Expansion<'a> { /// Struct or enum [`syn::Fields`]. fields: &'a syn::Fields, + /// Type parameters in this struct or enum. + type_params: &'a [&'a syn::Ident], + /// Name of the attributes, considered by this macro. attr_name: &'a syn::Ident, } @@ -275,7 +294,7 @@ impl<'a> Expansion<'a> { None => { let ident = format_ident!("_{i}"); Ok(quote! { - derive_more::__private::DebugTuple::field(#out, #ident) + derive_more::__private::DebugTuple::field(#out, &#ident) }) } }, @@ -316,7 +335,7 @@ impl<'a> Expansion<'a> { ) }), None => Ok(quote! { - derive_more::core::fmt::DebugStruct::field(#out, #field_str, #field_ident) + derive_more::core::fmt::DebugStruct::field(#out, #field_str, &#field_ident) }), } })?; @@ -334,15 +353,26 @@ impl<'a> Expansion<'a> { let mut out = self.attr.bounds.0.clone().into_iter().collect::>(); if let Some(fmt) = self.attr.fmt.as_ref() { - out.extend(fmt.bounded_types(self.fields).map(|(ty, trait_name)| { - let trait_ident = format_ident!("{trait_name}"); + out.extend(fmt.bounded_types(self.fields).filter_map( + |(ty, trait_name)| { + if !self.contains_generic_param(ty) { + return None; + } + + let trait_ident = format_ident!("{trait_name}"); - parse_quote! { #ty: derive_more::core::fmt::#trait_ident } - })); + Some(parse_quote! { #ty: derive_more::core::fmt::#trait_ident }) + }, + )); Ok(out) } else { self.fields.iter().try_fold(out, |mut out, field| { let ty = &field.ty; + + if !self.contains_generic_param(ty) { + return Ok(out); + } + match FieldAttribute::parse_attrs(&field.attrs, self.attr_name)? .map(Spanning::into_inner) { @@ -362,4 +392,105 @@ impl<'a> Expansion<'a> { }) } } + + /// Checks whether the provided [`syn::Path`] contains any of these [`Expansion::type_params`]. + fn path_contains_generic_param(&self, path: &syn::Path) -> bool { + path.segments + .iter() + .any(|segment| match &segment.arguments { + syn::PathArguments::None => false, + syn::PathArguments::AngleBracketed( + syn::AngleBracketedGenericArguments { args, .. }, + ) => args.iter().any(|generic| match generic { + syn::GenericArgument::Type(ty) + | syn::GenericArgument::AssocType(syn::AssocType { ty, .. }) => { + self.contains_generic_param(ty) + } + + syn::GenericArgument::Lifetime(_) + | syn::GenericArgument::Const(_) + | syn::GenericArgument::AssocConst(_) + | syn::GenericArgument::Constraint(_) => false, + _ => unimplemented!( + "syntax is not supported by `derive_more`, please report a bug", + ), + }), + syn::PathArguments::Parenthesized( + syn::ParenthesizedGenericArguments { inputs, output, .. }, + ) => { + inputs.iter().any(|ty| self.contains_generic_param(ty)) + || match output { + syn::ReturnType::Default => false, + syn::ReturnType::Type(_, ty) => { + self.contains_generic_param(ty) + } + } + } + }) + } + + /// Checks whether the provided [`syn::Type`] contains any of these [`Expansion::type_params`]. + fn contains_generic_param(&self, ty: &syn::Type) -> bool { + if self.type_params.is_empty() { + return false; + } + match ty { + syn::Type::Path(syn::TypePath { qself, path }) => { + if let Some(qself) = qself { + if self.contains_generic_param(&qself.ty) { + return true; + } + } + + if let Some(ident) = path.get_ident() { + self.type_params.iter().any(|param| *param == ident) + } else { + self.path_contains_generic_param(path) + } + } + + syn::Type::Array(syn::TypeArray { elem, .. }) + | syn::Type::Group(syn::TypeGroup { elem, .. }) + | syn::Type::Paren(syn::TypeParen { elem, .. }) + | syn::Type::Ptr(syn::TypePtr { elem, .. }) + | syn::Type::Reference(syn::TypeReference { elem, .. }) + | syn::Type::Slice(syn::TypeSlice { elem, .. }) => { + self.contains_generic_param(elem) + } + + syn::Type::BareFn(syn::TypeBareFn { inputs, output, .. }) => { + inputs + .iter() + .any(|arg| self.contains_generic_param(&arg.ty)) + || match output { + syn::ReturnType::Default => false, + syn::ReturnType::Type(_, ty) => self.contains_generic_param(ty), + } + } + syn::Type::Tuple(syn::TypeTuple { elems, .. }) => { + elems.iter().any(|ty| self.contains_generic_param(ty)) + } + + syn::Type::ImplTrait(_) => false, + syn::Type::Infer(_) => false, + syn::Type::Macro(_) => false, + syn::Type::Never(_) => false, + syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) => { + bounds.iter().any(|bound| match bound { + syn::TypeParamBound::Trait(syn::TraitBound { path, .. }) => { + self.path_contains_generic_param(path) + } + syn::TypeParamBound::Lifetime(_) => false, + syn::TypeParamBound::Verbatim(_) => false, + _ => unimplemented!( + "syntax is not supported by `derive_more`, please report a bug", + ), + }) + } + syn::Type::Verbatim(_) => false, + _ => unimplemented!( + "syntax is not supported by `derive_more`, please report a bug", + ), + } + } } diff --git a/impl/src/mul_assign_like.rs b/impl/src/mul_assign_like.rs index 8e3f2cb7..b5fb4e50 100644 --- a/impl/src/mul_assign_like.rs +++ b/impl/src/mul_assign_like.rs @@ -55,6 +55,7 @@ pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result for #input_type #ty_generics #where_clause { #[inline] + #[track_caller] fn #method_ident(&mut self, rhs: #scalar_ident) { #( #exprs; )* } diff --git a/impl/src/mul_like.rs b/impl/src/mul_like.rs index d06372f3..c8a2ffac 100644 --- a/impl/src/mul_like.rs +++ b/impl/src/mul_like.rs @@ -53,6 +53,7 @@ pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result #input_type #ty_generics { #body } diff --git a/impl/src/try_into.rs b/impl/src/try_into.rs index 46478270..a2ebce8b 100644 --- a/impl/src/try_into.rs +++ b/impl/src/try_into.rs @@ -104,7 +104,7 @@ pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result for (#(#reference_with_lifetime #original_types),*) #where_clause { - type Error = derive_more::TryIntoError<#reference_with_lifetime #input_type>; + type Error = derive_more::TryIntoError<#reference_with_lifetime #input_type #ty_generics>; #[inline] fn try_from( diff --git a/src/lib.rs b/src/lib.rs index 879de304..6230cdb8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,7 +42,6 @@ doc = include_str!("../README.md") )] #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(all(not(feature = "std"), feature = "error"), feature(error_in_core))] #![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(any(not(docsrs), ci), deny(rustdoc::all))] #![forbid(non_ascii_idents, unsafe_code)] @@ -137,169 +136,283 @@ macro_rules! re_export_traits(( #[cfg(all(feature = $feature, any(not(docsrs), ci)))] #[doc(hidden)] - pub use crate::$new_module_name::*; + pub use crate::all_traits_and_derives::$new_module_name::*; } ); -re_export_traits!( - "add", - add_traits, - core::ops, - Add, - BitAnd, - BitOr, - BitXor, - Sub, -); -re_export_traits!( - "add_assign", - add_assign_traits, - core::ops, - AddAssign, - BitAndAssign, - BitOrAssign, - BitXorAssign, - SubAssign, -); -re_export_traits!("as_ref", as_ref_traits, core::convert, AsMut, AsRef); -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); -re_export_traits!( - "display", - display_traits, - core::fmt, - Binary, - Display, - LowerExp, - LowerHex, - Octal, - Pointer, - UpperExp, - UpperHex, -); +mod all_traits_and_derives { + re_export_traits!( + "add", + add_traits, + core::ops, + Add, + BitAnd, + BitOr, + BitXor, + Sub, + ); + re_export_traits!( + "add_assign", + add_assign_traits, + core::ops, + AddAssign, + BitAndAssign, + BitOrAssign, + BitXorAssign, + SubAssign, + ); + re_export_traits!("as_ref", as_ref_traits, core::convert, AsMut, AsRef); + 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); + re_export_traits!( + "display", + display_traits, + core::fmt, + Binary, + Display, + LowerExp, + LowerHex, + Octal, + Pointer, + UpperExp, + UpperHex, + ); + + #[cfg(not(feature = "std"))] + re_export_traits!("error", error_traits, core::error, Error); + #[cfg(feature = "std")] + re_export_traits!("error", error_traits, std::error, Error); + + re_export_traits!("from", from_traits, core::convert, From); + + re_export_traits!("from_str", from_str_traits, core::str, FromStr); + + re_export_traits!("index", index_traits, core::ops, Index); + + re_export_traits!("index_mut", index_mut_traits, core::ops, IndexMut); + + re_export_traits!("into", into_traits, core::convert, Into); + + re_export_traits!( + "into_iterator", + into_iterator_traits, + core::iter, + IntoIterator, + ); + + re_export_traits!("mul", mul_traits, core::ops, Div, Mul, Rem, Shl, Shr); + + #[cfg(feature = "mul_assign")] + re_export_traits!( + "mul_assign", + mul_assign_traits, + core::ops, + DivAssign, + MulAssign, + RemAssign, + ShlAssign, + ShrAssign, + ); + + re_export_traits!("not", not_traits, core::ops, Neg, Not); + + re_export_traits!("sum", sum_traits, core::iter, Product, Sum); + + re_export_traits!("try_from", try_from_traits, core::convert, TryFrom); + + re_export_traits!("try_into", try_into_traits, core::convert, TryInto); + + // Now re-export our own derives by their exact name to overwrite any derives that the trait + // re-exporting might inadvertently pull into scope. + #[cfg(feature = "add")] + pub use derive_more_impl::{Add, BitAnd, BitOr, BitXor, Sub}; + + #[cfg(feature = "add_assign")] + pub use derive_more_impl::{ + AddAssign, BitAndAssign, BitOrAssign, BitXorAssign, SubAssign, + }; -#[cfg(not(feature = "std"))] -re_export_traits!("error", error_traits, core::error, Error); -#[cfg(feature = "std")] -re_export_traits!("error", error_traits, std::error, Error); + #[cfg(feature = "as_ref")] + pub use derive_more_impl::{AsMut, AsRef}; -re_export_traits!("from", from_traits, core::convert, From); + #[cfg(feature = "constructor")] + pub use derive_more_impl::Constructor; -re_export_traits!("from_str", from_str_traits, core::str, FromStr); + #[cfg(feature = "debug")] + pub use derive_more_impl::Debug; -re_export_traits!("index", index_traits, core::ops, Index); + #[cfg(feature = "deref")] + pub use derive_more_impl::Deref; -re_export_traits!("index_mut", index_mut_traits, core::ops, IndexMut); + #[cfg(feature = "deref_mut")] + pub use derive_more_impl::DerefMut; -re_export_traits!("into", into_traits, core::convert, Into); + #[cfg(feature = "display")] + pub use derive_more_impl::{ + Binary, Display, LowerExp, LowerHex, Octal, Pointer, UpperExp, UpperHex, + }; -re_export_traits!( - "into_iterator", - into_iterator_traits, - core::iter, - IntoIterator, -); + #[cfg(feature = "error")] + pub use derive_more_impl::Error; -re_export_traits!("mul", mul_traits, core::ops, Div, Mul, Rem, Shl, Shr); + #[cfg(feature = "from")] + pub use derive_more_impl::From; -#[cfg(feature = "mul_assign")] -re_export_traits!( - "mul_assign", - mul_assign_traits, - core::ops, - DivAssign, - MulAssign, - RemAssign, - ShlAssign, - ShrAssign, -); + #[cfg(feature = "from_str")] + pub use derive_more_impl::FromStr; + + #[cfg(feature = "index")] + pub use derive_more_impl::Index; + + #[cfg(feature = "index_mut")] + pub use derive_more_impl::IndexMut; + + #[cfg(feature = "into")] + pub use derive_more_impl::Into; -re_export_traits!("not", not_traits, core::ops, Neg, Not); + #[cfg(feature = "into_iterator")] + pub use derive_more_impl::IntoIterator; -re_export_traits!("sum", sum_traits, core::iter, Product, Sum); + #[cfg(feature = "is_variant")] + pub use derive_more_impl::IsVariant; -re_export_traits!("try_from", try_from_traits, core::convert, TryFrom); + #[cfg(feature = "mul")] + pub use derive_more_impl::{Div, Mul, Rem, Shl, Shr}; -re_export_traits!("try_into", try_into_traits, core::convert, TryInto); + #[cfg(feature = "mul_assign")] + pub use derive_more_impl::{DivAssign, MulAssign, RemAssign, ShlAssign, ShrAssign}; -// Now re-export our own derives by their exact name to overwrite any derives that the trait -// re-exporting might inadvertently pull into scope. + #[cfg(feature = "not")] + pub use derive_more_impl::{Neg, Not}; + + #[cfg(feature = "sum")] + pub use derive_more_impl::{Product, Sum}; + + #[cfg(feature = "try_from")] + pub use derive_more_impl::TryFrom; + + #[cfg(feature = "try_into")] + pub use derive_more_impl::TryInto; + + #[cfg(feature = "try_unwrap")] + pub use derive_more_impl::TryUnwrap; + + #[cfg(feature = "unwrap")] + pub use derive_more_impl::Unwrap; +} + +// Now re-export our own derives and the std traits by their exact name to make rust-analyzer +// recognize the #[doc(hidden)] flag. +// See issues: +// 1. https://github.com/rust-lang/rust-analyzer/issues/11698 +// 2. https://github.com/rust-lang/rust-analyzer/issues/14079 #[cfg(feature = "add")] -pub use derive_more_impl::{Add, BitAnd, BitOr, BitXor, Sub}; +#[doc(hidden)] +pub use all_traits_and_derives::{Add, BitAnd, BitOr, BitXor, Sub}; #[cfg(feature = "add_assign")] -pub use derive_more_impl::{ +#[doc(hidden)] +pub use all_traits_and_derives::{ AddAssign, BitAndAssign, BitOrAssign, BitXorAssign, SubAssign, }; #[cfg(feature = "as_ref")] -pub use derive_more_impl::{AsMut, AsRef}; +#[doc(hidden)] +pub use all_traits_and_derives::{AsMut, AsRef}; #[cfg(feature = "constructor")] -pub use derive_more_impl::Constructor; +#[doc(hidden)] +pub use all_traits_and_derives::Constructor; #[cfg(feature = "debug")] -pub use derive_more_impl::Debug; +#[doc(hidden)] +pub use all_traits_and_derives::Debug; #[cfg(feature = "deref")] -pub use derive_more_impl::Deref; +#[doc(hidden)] +pub use all_traits_and_derives::Deref; #[cfg(feature = "deref_mut")] -pub use derive_more_impl::DerefMut; +#[doc(hidden)] +pub use all_traits_and_derives::DerefMut; #[cfg(feature = "display")] -pub use derive_more_impl::{ +#[doc(hidden)] +pub use all_traits_and_derives::{ Binary, Display, LowerExp, LowerHex, Octal, Pointer, UpperExp, UpperHex, }; #[cfg(feature = "error")] -pub use derive_more_impl::Error; +#[doc(hidden)] +pub use all_traits_and_derives::Error; #[cfg(feature = "from")] -pub use derive_more_impl::From; +#[doc(hidden)] +pub use all_traits_and_derives::From; #[cfg(feature = "from_str")] -pub use derive_more_impl::FromStr; +#[doc(hidden)] +pub use all_traits_and_derives::FromStr; #[cfg(feature = "index")] -pub use derive_more_impl::Index; +#[doc(hidden)] +pub use all_traits_and_derives::Index; #[cfg(feature = "index_mut")] -pub use derive_more_impl::IndexMut; +#[doc(hidden)] +pub use all_traits_and_derives::IndexMut; #[cfg(feature = "into")] -pub use derive_more_impl::Into; +#[doc(hidden)] +pub use all_traits_and_derives::Into; #[cfg(feature = "into_iterator")] -pub use derive_more_impl::IntoIterator; +#[doc(hidden)] +pub use all_traits_and_derives::IntoIterator; #[cfg(feature = "is_variant")] -pub use derive_more_impl::IsVariant; +#[doc(hidden)] +pub use all_traits_and_derives::IsVariant; #[cfg(feature = "mul")] -pub use derive_more_impl::{Div, Mul, Rem, Shl, Shr}; +#[doc(hidden)] +pub use all_traits_and_derives::{Div, Mul, Rem, Shl, Shr}; #[cfg(feature = "mul_assign")] -pub use derive_more_impl::{DivAssign, MulAssign, RemAssign, ShlAssign, ShrAssign}; +#[doc(hidden)] +pub use all_traits_and_derives::{ + DivAssign, MulAssign, RemAssign, ShlAssign, ShrAssign, +}; #[cfg(feature = "not")] -pub use derive_more_impl::{Neg, Not}; +#[doc(hidden)] +pub use all_traits_and_derives::{Neg, Not}; #[cfg(feature = "sum")] -pub use derive_more_impl::{Product, Sum}; +#[doc(hidden)] +pub use all_traits_and_derives::{Product, Sum}; #[cfg(feature = "try_from")] -pub use derive_more_impl::TryFrom; +#[doc(hidden)] +pub use all_traits_and_derives::TryFrom; #[cfg(feature = "try_into")] -pub use derive_more_impl::TryInto; +#[doc(hidden)] +pub use all_traits_and_derives::TryInto; #[cfg(feature = "try_unwrap")] -pub use derive_more_impl::TryUnwrap; +#[doc(hidden)] +pub use all_traits_and_derives::TryUnwrap; #[cfg(feature = "unwrap")] -pub use derive_more_impl::Unwrap; +#[doc(hidden)] +pub use all_traits_and_derives::Unwrap; + +// Re-export the derive macros again to show docs for our derives (but not for traits). This is +// done using a glob import to not hit E0252. +#[allow(unused_imports)] +pub use derive_more_impl::*; // Check if any feature is enabled #[cfg(not(any( diff --git a/tests/debug.rs b/tests/debug.rs index 15e7f12f..0c4b180a 100644 --- a/tests/debug.rs +++ b/tests/debug.rs @@ -1918,7 +1918,7 @@ mod generic { // See: https://github.com/JelteF/derive_more/issues/301 mod complex_enum_syntax { #[cfg(not(feature = "std"))] - use alloc::{boxed::Box, format}; + use alloc::format; use derive_more::Debug; @@ -1932,3 +1932,141 @@ mod complex_enum_syntax { assert_eq!(format!("{:?}", Enum::A), "A"); } } + +// See: https://github.com/JelteF/derive_more/issues/363 +mod type_variables { + mod our_alloc { + #[cfg(not(feature = "std"))] + pub use alloc::{boxed::Box, format, vec, vec::Vec}; + #[cfg(feature = "std")] + pub use std::{boxed::Box, format, vec, vec::Vec}; + } + + use our_alloc::{format, vec, Box, Vec}; + + use derive_more::Debug; + + #[derive(Debug)] + struct ItemStruct { + next: Option>, + } + + #[derive(Debug)] + struct ItemTuple(Option>); + + #[derive(Debug)] + #[debug("Item({_0:?})")] + struct ItemTupleContainerFmt(Option>); + + #[derive(Debug)] + enum ItemEnum { + Node { children: Vec, inner: i32 }, + Leaf { inner: i32 }, + } + + #[derive(Debug)] + struct VecMeansDifferent { + next: our_alloc::Vec, + real: Vec, + } + + #[derive(Debug)] + struct Array { + #[debug("{t}")] + t: [T; 10], + } + + mod parens { + #![allow(unused_parens)] // test that type is found even in parentheses + + use derive_more::Debug; + + #[derive(Debug)] + struct Paren { + t: (T), + } + } + + #[derive(Debug)] + struct ParenthesizedGenericArgumentsInput { + t: dyn Fn(T) -> i32, + } + + #[derive(Debug)] + struct ParenthesizedGenericArgumentsOutput { + t: dyn Fn(i32) -> T, + } + + #[derive(Debug)] + struct Ptr { + t: *const T, + } + + #[derive(Debug)] + struct Reference<'a, T> { + t: &'a T, + } + + #[derive(Debug)] + struct Slice<'a, T> { + t: &'a [T], + } + + #[derive(Debug)] + struct BareFn { + t: Box T>, + } + + #[derive(Debug)] + struct Tuple { + t: Box<(T, T)>, + } + + trait MyTrait {} + + #[derive(Debug)] + struct TraitObject { + t: Box>, + } + + #[test] + fn assert() { + assert_eq!( + format!( + "{:?}", + ItemStruct { + next: Some(Box::new(ItemStruct { next: None })) + }, + ), + "ItemStruct { next: Some(ItemStruct { next: None }) }", + ); + + assert_eq!( + format!("{:?}", ItemTuple(Some(Box::new(ItemTuple(None))))), + "ItemTuple(Some(ItemTuple(None)))", + ); + + assert_eq!( + format!( + "{:?}", + ItemTupleContainerFmt(Some(Box::new(ItemTupleContainerFmt(None)))), + ), + "Item(Some(Item(None)))", + ); + + let item = ItemEnum::Node { + children: vec![ + ItemEnum::Node { + children: vec![], + inner: 0, + }, + ItemEnum::Leaf { inner: 1 }, + ], + inner: 2, + }; + assert_eq!( + format!("{item:?}"), + "Node { children: [Node { children: [], inner: 0 }, Leaf { inner: 1 }], inner: 2 }", + ) + } +} diff --git a/tests/deref_mut.rs b/tests/deref_mut.rs index dab6733f..635f63b6 100644 --- a/tests/deref_mut.rs +++ b/tests/deref_mut.rs @@ -5,7 +5,7 @@ extern crate alloc; #[cfg(not(feature = "std"))] -use alloc::{boxed::Box, format, vec, vec::Vec}; +use alloc::{boxed::Box, vec, vec::Vec}; use derive_more::DerefMut; diff --git a/tests/display.rs b/tests/display.rs index ebc0012e..9a91afbd 100644 --- a/tests/display.rs +++ b/tests/display.rs @@ -5,12 +5,7 @@ extern crate alloc; #[cfg(not(feature = "std"))] -use alloc::{ - boxed::Box, - format, - string::{String, ToString}, - vec::Vec, -}; +use alloc::{format, string::ToString}; use derive_more::{ Binary, Display, LowerExp, LowerHex, Octal, Pointer, UpperExp, UpperHex, diff --git a/tests/generics.rs b/tests/generics.rs index bf968eb5..42c568a4 100644 --- a/tests/generics.rs +++ b/tests/generics.rs @@ -3,7 +3,7 @@ use derive_more::{ Add, AddAssign, Constructor, Deref, DerefMut, Display, Error, From, FromStr, Index, - IndexMut, IntoIterator, Mul, MulAssign, Not, Sum, + IndexMut, IntoIterator, Mul, MulAssign, Not, Sum, TryInto, }; #[derive( @@ -262,3 +262,11 @@ struct StructLifetimeGenericBoundsConstDefault< > { inner: &'lt E, } + +#[derive(Debug, Display)] +struct Wrapper<'a, const Y: usize, U>(&'a [U; Y]); + +#[derive(Debug, Display, TryInto)] +enum Foo<'lt: 'static, T: Clone, const X: usize> { + X(Wrapper<'lt, X, T>), +} diff --git a/tests/lib.rs b/tests/lib.rs index 783edd52..59632fc0 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -248,7 +248,10 @@ fn main() { assert_eq!(MyInt(50), MyInt(5) * 10); assert_eq!(DoubleUInt(5, 6) * 10, DoubleUInt(50, 60)); - // assert_eq!(DoubleUIntStruct{x:5, y:6} * 10, DoubleUIntStruct{x:50, y:60}); + assert_eq!( + DoubleUIntStruct { x: 5, y: 6 } * 10, + DoubleUIntStruct { x: 50, y: 60 } + ); let mut myint = MyInt(5); assert_eq!(5, *myint); diff --git a/tests/no_std.rs b/tests/no_std.rs index 7d2adf14..566fe13a 100644 --- a/tests/no_std.rs +++ b/tests/no_std.rs @@ -74,3 +74,58 @@ enum EnumWithUnit { SmallInt(i32), Unit, } + +#[rustversion::nightly] +mod error { + use derive_more::{Display, Error, From}; + #[derive(Default, Debug, Display, Error)] + struct Simple; + + #[derive(Default, Debug, Display, Error)] + struct WithSource { + source: Simple, + } + #[derive(Default, Debug, Display, Error)] + struct WithExplicitSource { + #[error(source)] + explicit_source: Simple, + } + + #[derive(Default, Debug, Display, Error)] + struct Tuple(Simple); + + #[derive(Default, Debug, Display, Error)] + struct WithoutSource(#[error(not(source))] i32); + #[derive(Debug, Display, Error, From)] + enum CompoundError { + Simple, + WithSource { + source: Simple, + }, + WithExplicitSource { + #[error(source)] + explicit_source: WithSource, + }, + Tuple(WithExplicitSource), + WithoutSource(#[error(not(source))] Tuple), + } + + #[test] + fn assert() { + assert!(Simple.source().is_none()); + assert!(WithSource::default().source().is_some()); + assert!(WithExplicitSource::default().source().is_some()); + assert!(Tuple::default().source().is_some()); + assert!(Tuple::default().source().is_some()); + assert!(WithoutSource::default().source().is_none()); + assert!(CompoundError::Simple.source().is_none()); + assert!(CompoundError::from(Simple).source().is_some()); + assert!(CompoundError::from(WithSource::default()) + .source() + .is_some()); + assert!(CompoundError::from(WithExplicitSource::default()) + .source() + .is_some()); + assert!(CompoundError::from(Tuple::default()).source().is_none()); + } +} diff --git a/tests/sum.rs b/tests/sum.rs index a273bf80..503d6b4f 100644 --- a/tests/sum.rs +++ b/tests/sum.rs @@ -1,4 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] +#![allow(dead_code)] // some code is tested for type checking only use derive_more::Sum; diff --git a/tests/try_into.rs b/tests/try_into.rs index 37485eff..95b510eb 100644 --- a/tests/try_into.rs +++ b/tests/try_into.rs @@ -40,6 +40,13 @@ enum MixedInts { Unit2, } +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +struct Wrapper<'a, const Y: usize, U>(&'a [U; Y]); + +enum Foo<'lt: 'static, T: Clone, const X: usize> { + X(Wrapper<'lt, X, T>), +} + #[test] fn test_try_into() { let mut i = MixedInts::SmallInt(42);