diff --git a/CHANGELOG.md b/CHANGELOG.md index 19f0e070..9d1c235d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed +- Associated types of type parameters not being treated as generics in `Debug` + and `Display` expansions. + ([#399](https://github.com/JelteF/derive_more/pull/399)) +- `unreachable_code` warnings on generated code when `!` (never type) is used. + ([#404](https://github.com/JelteF/derive_more/pull/404)) - A top level `#[display("...")]` attribute on an enum being incorrectly treated as transparent. ([#395](https://github.com/JelteF/derive_more/pull/395)) diff --git a/impl/src/as/mod.rs b/impl/src/as/mod.rs index fd631aa3..5157fe86 100644 --- a/impl/src/as/mod.rs +++ b/impl/src/as/mod.rs @@ -280,6 +280,7 @@ impl<'a> ToTokens for Expansion<'a> { }; quote! { + #[allow(unreachable_code)] // omit warnings for `!` and other unreachable types #[automatically_derived] impl #impl_gens #trait_ty for #ty_ident #ty_gens #where_clause { #[inline] diff --git a/impl/src/constructor.rs b/impl/src/constructor.rs index 284b0643..c1c77cb3 100644 --- a/impl/src/constructor.rs +++ b/impl/src/constructor.rs @@ -26,6 +26,7 @@ pub fn expand(input: &DeriveInput, _: &str) -> TokenStream { let original_types = &get_field_types(&fields); quote! { #[allow(missing_docs)] + #[allow(unreachable_code)] // omit warnings for `!` and other unreachable types #[automatically_derived] impl #impl_generics #input_type #ty_generics #where_clause { #[inline] diff --git a/impl/src/deref.rs b/impl/src/deref.rs index f87c64cc..cbfc5bdc 100644 --- a/impl/src/deref.rs +++ b/impl/src/deref.rs @@ -42,6 +42,7 @@ pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result Result syn::Result { }; Ok(quote! { + #[allow(unreachable_code)] // omit warnings for `!` and other unreachable types #[automatically_derived] impl #impl_gens derive_more::Debug for #ident #ty_gens #where_clause { #[inline] diff --git a/impl/src/fmt/display.rs b/impl/src/fmt/display.rs index 514195a8..a6037a6e 100644 --- a/impl/src/fmt/display.rs +++ b/impl/src/fmt/display.rs @@ -62,6 +62,7 @@ pub fn expand(input: &syn::DeriveInput, trait_name: &str) -> syn::Result false, + .enumerate() + .any(|(n, segment)| match &segment.arguments { + syn::PathArguments::None => { + // `TypeParam::AssocType` case. + (n == 0) && type_params.contains(&&segment.ident) + } syn::PathArguments::AngleBracketed( syn::AngleBracketedGenericArguments { args, .. }, ) => args.iter().any(|generic| match generic { diff --git a/impl/src/from.rs b/impl/src/from.rs index c784d972..10539ee3 100644 --- a/impl/src/from.rs +++ b/impl/src/from.rs @@ -172,6 +172,7 @@ impl<'a> Expansion<'a> { }); Ok(quote! { + #[allow(unreachable_code)] // omit warnings for `!` and unreachable types #[automatically_derived] impl #impl_gens derive_more::From<#ty> for #ident #ty_gens #where_clause { #[inline] @@ -192,6 +193,7 @@ impl<'a> Expansion<'a> { }); Ok(quote! { + #[allow(unreachable_code)] // omit warnings for `!` and other unreachable types #[automatically_derived] impl #impl_gens derive_more::From<(#( #field_tys ),*)> for #ident #ty_gens #where_clause { #[inline] @@ -222,9 +224,10 @@ impl<'a> Expansion<'a> { let generics = { let mut generics = self.generics.clone(); for (ty, ident) in field_tys.iter().zip(&gen_idents) { - generics.make_where_clause().predicates.push( - parse_quote! { #ty: derive_more::From<#ident> }, - ); + generics + .make_where_clause() + .predicates + .push(parse_quote! { #ty: derive_more::From<#ident> }); generics .params .push(syn::TypeParam::from(ident.clone()).into()); @@ -234,6 +237,7 @@ impl<'a> Expansion<'a> { let (impl_gens, _, where_clause) = generics.split_for_impl(); Ok(quote! { + #[allow(unreachable_code)] // omit warnings for `!` and other unreachable types #[automatically_derived] impl #impl_gens derive_more::From<(#( #gen_idents ),*)> for #ident #ty_gens #where_clause { #[inline] diff --git a/impl/src/is_variant.rs b/impl/src/is_variant.rs index 12f7db1e..86d1de49 100644 --- a/impl/src/is_variant.rs +++ b/impl/src/is_variant.rs @@ -53,6 +53,7 @@ pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result TokenStream { }; quote! { + #[allow(unreachable_code)] // omit warnings for `!` and other unreachable types #[automatically_derived] impl #impl_generics derive_more::#trait_ident for #input_type #ty_generics #where_clause { type Output = #output_type; diff --git a/impl/src/try_unwrap.rs b/impl/src/try_unwrap.rs index b90ba714..28187962 100644 --- a/impl/src/try_unwrap.rs +++ b/impl/src/try_unwrap.rs @@ -123,6 +123,7 @@ pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result Result>, } + #[derive(Debug)] + struct AssocType { + iter: I, + elem: Option, + } + + #[derive(derive_more::Debug)] + struct CollidedPathName { + item: Item, + elem: Option, + } + + mod some_path { + use super::Debug; + + #[derive(Debug)] + pub struct Item; + } + #[test] fn assert() { assert_eq!( @@ -2148,6 +2219,28 @@ mod type_variables { assert_eq!( format!("{item:?}"), "Node { children: [Node { children: [], inner: 0 }, Leaf { inner: 1 }], inner: 2 }", - ) + ); + + assert_eq!( + format!( + "{:?}", + AssocType { + iter: iter::empty::(), + elem: None, + }, + ), + "AssocType { iter: Empty, elem: None }", + ); + + assert_eq!( + format!( + "{:?}", + CollidedPathName { + item: true, + elem: None, + }, + ), + "CollidedPathName { item: true, elem: None }", + ); } } diff --git a/tests/deref.rs b/tests/deref.rs index efdb6d2d..9feab5f3 100644 --- a/tests/deref.rs +++ b/tests/deref.rs @@ -1,4 +1,5 @@ #![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"))] @@ -73,3 +74,16 @@ fn deref_generic_forward() { let boxed = GenericBox(Box::new(1i32)); assert_eq!(*boxed, 1i32); } + +#[cfg(nightly)] +mod never { + use super::*; + + #[derive(Deref)] + struct Tuple(!); + + #[derive(Deref)] + struct Struct { + field: !, + } +} diff --git a/tests/deref_mut.rs b/tests/deref_mut.rs index 635f63b6..2ed1bb0b 100644 --- a/tests/deref_mut.rs +++ b/tests/deref_mut.rs @@ -1,4 +1,5 @@ #![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"))] @@ -135,3 +136,34 @@ fn deref_mut_generic_forward() { *boxed = 3; assert_eq!(*boxed, 3i32); } + +#[cfg(nightly)] +mod never { + use super::*; + + #[derive(DerefMut)] + struct Tuple(!); + + // `Deref` implementation is required for `DerefMut`. + impl ::core::ops::Deref for Tuple { + type Target = !; + #[inline] + fn deref(&self) -> &Self::Target { + self.0 + } + } + + #[derive(DerefMut)] + struct Struct { + field: !, + } + + // `Deref` implementation is required for `DerefMut`. + impl ::core::ops::Deref for Struct { + type Target = !; + #[inline] + fn deref(&self) -> &Self::Target { + self.field + } + } +} diff --git a/tests/display.rs b/tests/display.rs index 4d10fc29..456831e9 100644 --- a/tests/display.rs +++ b/tests/display.rs @@ -1,4 +1,5 @@ #![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"))] @@ -528,6 +529,19 @@ mod structs { } } } + + #[cfg(nightly)] + mod never { + use super::*; + + #[derive(Display)] + struct Tuple(!); + + #[derive(Display)] + struct Struct { + field: !, + } + } } mod multi_field { @@ -682,6 +696,22 @@ mod structs { } } } + + #[cfg(nightly)] + mod never { + use super::*; + + #[derive(Display)] + #[display("{_0}")] + struct Tuple(i32, !); + + #[derive(Display)] + #[display("{field}")] + struct Struct { + field: !, + other: i32, + } + } } } @@ -1154,6 +1184,17 @@ mod enums { } } } + + #[cfg(nightly)] + mod never { + use super::*; + + #[derive(Display)] + enum Enum { + Unnamed(!), + Named { field: ! }, + } + } } mod multi_field_variant { @@ -1314,6 +1355,19 @@ mod enums { } } + #[cfg(nightly)] + mod never { + use super::*; + + #[derive(Display)] + enum Enum { + #[display("{_0}")] + Unnamed(i32, !), + #[display("{field}")] + Named { field: !, other: i32 }, + } + } + mod shared_format { use super::*; @@ -2433,11 +2487,13 @@ mod type_variables { mod our_alloc { #[cfg(not(feature = "std"))] pub use alloc::{boxed::Box, format, vec::Vec}; + #[cfg(not(feature = "std"))] + pub use core::iter; #[cfg(feature = "std")] - pub use std::{boxed::Box, format, vec::Vec}; + pub use std::{boxed::Box, format, iter, vec::Vec}; } - use our_alloc::{format, Box}; + use our_alloc::{format, iter, Box}; // We want `Vec` in scope to test that code generation works if it is there. #[allow(unused_imports)] @@ -2557,6 +2613,25 @@ mod type_variables { t: Box>, } + #[derive(Display)] + #[display("{iter:?} with {elem:?}")] + struct AssocType { + iter: I, + elem: Option, + } + + #[derive(Display)] + #[display("{item:?} with {elem:?}")] + struct CollidedPathName { + item: Item, + elem: Option, + } + + mod some_path { + #[derive(Debug)] + pub struct Item; + } + #[test] fn assert() { assert_eq!( @@ -2605,6 +2680,28 @@ mod type_variables { assert_eq!( format!("{item}"), "Some(Variant2 { next: OptionalBox { inner: None } })", - ) + ); + + assert_eq!( + format!( + "{}", + AssocType { + iter: iter::empty::(), + elem: None, + }, + ), + "Empty with None", + ); + + assert_eq!( + format!( + "{}", + CollidedPathName { + item: false, + elem: Some(some_path::Item), + }, + ), + "false with Some(Item)", + ); } } diff --git a/tests/from.rs b/tests/from.rs index 48f745fb..51cc6374 100644 --- a/tests/from.rs +++ b/tests/from.rs @@ -1,4 +1,5 @@ #![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"))] @@ -455,6 +456,20 @@ mod structs { } } } + + #[cfg(nightly)] + mod never { + use super::*; + + #[derive(From)] + struct Tuple(i32, !); + + #[derive(From)] + struct Struct { + field1: !, + field2: i16, + } + } } } @@ -1787,5 +1802,16 @@ mod enums { ); } } + + #[cfg(nightly)] + mod never { + use super::*; + + #[derive(From)] + enum Enum { + Tuple(i8, !), + Struct { field1: !, field2: i16 }, + } + } } } diff --git a/tests/is_variant.rs b/tests/is_variant.rs index 62ce2c83..d901258c 100644 --- a/tests/is_variant.rs +++ b/tests/is_variant.rs @@ -1,4 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(nightly, feature(never_type))] #![allow(dead_code)] // some code is tested for type checking only use derive_more::IsVariant; @@ -161,3 +162,16 @@ const _: () = { assert!(!ks.is_never_mind()); assert!(ks.is_nothing_to_see_here()); }; + +#[cfg(nightly)] +mod never { + use super::*; + + #[derive(IsVariant)] + enum Enum { + Tuple(!), + Struct { field: ! }, + TupleMulti(i32, !), + StructMulti { field: !, other: i32 }, + } +} diff --git a/tests/not.rs b/tests/not.rs index 96b4bb3a..31453bde 100644 --- a/tests/not.rs +++ b/tests/not.rs @@ -1,4 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(nightly, feature(never_type))] #![allow(dead_code)] // some code is tested for type checking only use derive_more::Not; @@ -27,3 +28,33 @@ enum EnumWithUnit { SmallInt(i32), Unit, } + +#[cfg(nightly)] +mod never { + use super::*; + + #[derive(Not)] + struct Tuple(!); + + #[derive(Not)] + struct Struct { + field: !, + } + + #[derive(Not)] + struct TupleMulti(i32, !); + + #[derive(Not)] + struct StructMulti { + field: !, + other: i32, + } + + #[derive(Not)] + enum Enum { + Tuple(!), + Struct { field: ! }, + TupleMulti(i32, !), + StructMulti { field: !, other: i32 }, + } +} diff --git a/tests/try_unwrap.rs b/tests/try_unwrap.rs index e8a2dd8b..03ce7bd1 100644 --- a/tests/try_unwrap.rs +++ b/tests/try_unwrap.rs @@ -1,4 +1,5 @@ #![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"))] @@ -128,3 +129,14 @@ pub fn test_try_unwrap_mut_2() { assert_eq!(value, Tuple::Double(255, 256)); } + +#[cfg(nightly)] +mod never { + use super::*; + + #[derive(TryUnwrap)] + enum Enum { + Tuple(!), + TupleMulti(i32, !), + } +} diff --git a/tests/unwrap.rs b/tests/unwrap.rs index ebff8667..018e6acb 100644 --- a/tests/unwrap.rs +++ b/tests/unwrap.rs @@ -1,4 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(nightly, feature(never_type))] #![allow(dead_code)] // some code is tested for type checking only use derive_more::Unwrap; @@ -117,3 +118,14 @@ pub fn test_unwrap_mut_2() { assert_eq!(value, Tuple::Single(256)); } + +#[cfg(nightly)] +mod never { + use super::*; + + #[derive(Unwrap)] + enum Enum { + Tuple(!), + TupleMulti(i32, !), + } +}