Skip to content

Commit

Permalink
Fix ambiguous associated item for TryFrom, TryInto and FromStr (#…
Browse files Browse the repository at this point in the history
…410)

Resolves #409

## Synopsis

Fixes the "abiguous associated item" error when deriving `TryFrom`,
`TryInto` or `FromStr` when those types have an associated item called
`Error` or `Err` respectively.

All these derives produce that error:
```rust
use derive_more::*;

#[derive(TryFrom)]
#[try_from(repr)]
#[repr(u8)]
enum LogLevel {
    Error,
}

#[derive(FromStr)]
enum EnumNoFields {
    Err,
}

#[derive(TryInto)]
enum MixedInts {
    Foo(LogLevel),
}
```

## Solution

Replace  `Self::Error` with the type that's behind it.
  • Loading branch information
bluurryy authored Sep 8, 2024
1 parent c8a9f8f commit 71df46c
Show file tree
Hide file tree
Showing 9 changed files with 49 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
([#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))
- Fix ambiguous associated item error when deriving `TryFrom`, `TryInto` or `FromStr` with an associated item called `Error` or `Err` respectively.


## 1.0.0 - 2024-08-07
Expand Down
6 changes: 3 additions & 3 deletions impl/doc/from_str.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Code like this will be generated:
# struct MyInt(i32);
impl derive_more::FromStr for MyInt {
type Err = <i32 as derive_more::FromStr>::Err;
fn from_str(src: &str) -> Result<Self, Self::Err> {
fn from_str(src: &str) -> Result<Self, <i32 as derive_more::FromStr>::Err> {
return Ok(MyInt(i32::from_str(src)?));
}
}
Expand Down Expand Up @@ -76,7 +76,7 @@ Code like this will be generated:
# }
impl derive_more::FromStr for Point1D {
type Err = <i32 as derive_more::FromStr>::Err;
fn from_str(src: &str) -> Result<Self, Self::Err> {
fn from_str(src: &str) -> Result<Self, <i32 as derive_more::FromStr>::Err> {
return Ok(Point1D {
x: i32::from_str(src)?,
});
Expand Down Expand Up @@ -123,7 +123,7 @@ Code like this will be generated:
#
impl derive_more::FromStr for EnumNoFields {
type Err = derive_more::FromStrError;
fn from_str(src: &str) -> Result<Self, Self::Err> {
fn from_str(src: &str) -> Result<Self, derive_more::FromStrError> {
Ok(match src.to_lowercase().as_str() {
"foo" => EnumNoFields::Foo,
"bar" => EnumNoFields::Bar,
Expand Down
14 changes: 7 additions & 7 deletions impl/doc/try_into.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ Code like this will be generated:
# }
impl derive_more::TryFrom<MixedInts> for (i32) {
type Error = &'static str;
fn try_from(value: MixedInts) -> Result<Self, Self::Error> {
fn try_from(value: MixedInts) -> Result<Self, &'static str> {
match value {
MixedInts::SmallInt(__0) => Ok(__0),
_ => Err("Only SmallInt can be converted to i32"),
Expand All @@ -106,7 +106,7 @@ impl derive_more::TryFrom<MixedInts> for (i32) {
}
impl derive_more::TryFrom<MixedInts> for (i64) {
type Error = &'static str;
fn try_from(value: MixedInts) -> Result<Self, Self::Error> {
fn try_from(value: MixedInts) -> Result<Self, &'static str> {
match value {
MixedInts::BigInt(__0) => Ok(__0),
_ => Err("Only BigInt can be converted to i64"),
Expand All @@ -115,7 +115,7 @@ impl derive_more::TryFrom<MixedInts> for (i64) {
}
impl derive_more::TryFrom<MixedInts> for (i32, i32) {
type Error = &'static str;
fn try_from(value: MixedInts) -> Result<Self, Self::Error> {
fn try_from(value: MixedInts) -> Result<Self, &'static str> {
match value {
MixedInts::TwoSmallInts(__0, __1) => Ok((__0, __1)),
_ => Err("Only TwoSmallInts can be converted to (i32, i32)"),
Expand All @@ -124,7 +124,7 @@ impl derive_more::TryFrom<MixedInts> for (i32, i32) {
}
impl derive_more::TryFrom<MixedInts> for (i64, i64) {
type Error = &'static str;
fn try_from(value: MixedInts) -> Result<Self, Self::Error> {
fn try_from(value: MixedInts) -> Result<Self, &'static str> {
match value {
MixedInts::NamedSmallInts { x: __0, y: __1 } => Ok((__0, __1)),
_ => Err("Only NamedSmallInts can be converted to (i64, i64)"),
Expand All @@ -133,7 +133,7 @@ impl derive_more::TryFrom<MixedInts> for (i64, i64) {
}
impl derive_more::TryFrom<MixedInts> for (u32) {
type Error = &'static str;
fn try_from(value: MixedInts) -> Result<Self, Self::Error> {
fn try_from(value: MixedInts) -> Result<Self, &'static str> {
match value {
MixedInts::UnsignedOne(__0) | MixedInts::UnsignedTwo(__0) => Ok(__0),
_ => Err("Only UnsignedOne, UnsignedTwo can be converted to u32"),
Expand Down Expand Up @@ -163,7 +163,7 @@ Code like this will be generated:
# }
impl derive_more::TryFrom<EnumWithUnit> for (i32) {
type Error = &'static str;
fn try_from(value: EnumWithUnit) -> Result<Self, Self::Error> {
fn try_from(value: EnumWithUnit) -> Result<Self, &'static str> {
match value {
EnumWithUnit::SmallInt(__0) => Ok(__0),
_ => Err("Only SmallInt can be converted to i32"),
Expand All @@ -172,7 +172,7 @@ impl derive_more::TryFrom<EnumWithUnit> for (i32) {
}
impl derive_more::TryFrom<EnumWithUnit> for () {
type Error = &'static str;
fn try_from(value: EnumWithUnit) -> Result<Self, Self::Error> {
fn try_from(value: EnumWithUnit) -> Result<Self, &'static str> {
match value {
EnumWithUnit::Unit => Ok(()),
_ => Err("Only Unit can be converted to ()"),
Expand Down
7 changes: 4 additions & 3 deletions impl/src/from_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,15 @@ pub fn struct_from(state: &State, trait_name: &'static str) -> TokenStream {

let initializers = [quote! { #casted_trait::from_str(src)? }];
let body = single_field_data.initializer(&initializers);
let error = quote! { <#field_type as #trait_path>::Err };

quote! {
#[automatically_derived]
impl #impl_generics #trait_path for #input_type #ty_generics #where_clause {
type Err = <#field_type as #trait_path>::Err;
type Err = #error;

#[inline]
fn from_str(src: &str) -> derive_more::core::result::Result<Self, Self::Err> {
fn from_str(src: &str) -> derive_more::core::result::Result<Self, #error> {
derive_more::core::result::Result::Ok(#body)
}
}
Expand Down Expand Up @@ -97,7 +98,7 @@ fn enum_from(
type Err = derive_more::FromStrError;

#[inline]
fn from_str(src: &str) -> derive_more::core::result::Result<Self, Self::Err> {
fn from_str(src: &str) -> derive_more::core::result::Result<Self, derive_more::FromStrError> {
Ok(match src.to_lowercase().as_str() {
#(#cases)*
_ => return Err(derive_more::FromStrError::new(#input_type_name)),
Expand Down
6 changes: 4 additions & 2 deletions impl/src/try_from.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,16 @@ impl ToTokens for Expansion {
)
.unzip();

let error = quote! { derive_more::TryFromReprError<#repr_ty> };

quote! {
#[automatically_derived]
impl #impl_generics derive_more::TryFrom<#repr_ty #ty_generics> for #ident #where_clause {
type Error = derive_more::TryFromReprError<#repr_ty>;
type Error = #error;

#[allow(non_upper_case_globals)]
#[inline]
fn try_from(val: #repr_ty) -> derive_more::core::result::Result<Self, Self::Error> {
fn try_from(val: #repr_ty) -> derive_more::core::result::Result<Self, #error> {
#( const #consts: #repr_ty = #discriminants; )*
match val {
#(#consts => derive_more::core::result::Result::Ok(#ident::#variants),)*
Expand Down
8 changes: 6 additions & 2 deletions impl/src/try_into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,21 @@ pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStre
input.generics.split_for_impl()
};

let error = quote! {
derive_more::TryIntoError<#reference_with_lifetime #input_type #ty_generics>
};

let try_from = quote! {
#[automatically_derived]
impl #impl_generics derive_more::core::convert::TryFrom<
#reference_with_lifetime #input_type #ty_generics
> for (#(#reference_with_lifetime #original_types),*) #where_clause {
type Error = derive_more::TryIntoError<#reference_with_lifetime #input_type #ty_generics>;
type Error = #error;

#[inline]
fn try_from(
value: #reference_with_lifetime #input_type #ty_generics,
) -> derive_more::core::result::Result<Self, Self::Error> {
) -> derive_more::core::result::Result<Self, #error> {
match value {
#(#matchers)|* => derive_more::core::result::Result::Ok(#vars),
_ => derive_more::core::result::Result::Err(
Expand Down
6 changes: 6 additions & 0 deletions tests/from_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ struct Point1D {
x: i32,
}

/// Making sure that `FromStr` does not trigger an ambiguous associated item error for `Err`.
#[derive(FromStr)]
enum EnumWithErr {
Err,
}

#[derive(Debug, FromStr, PartialEq, Eq)]
enum EnumNoFields {
Foo,
Expand Down
8 changes: 8 additions & 0 deletions tests/try_from.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@

use derive_more::TryFrom;

/// Making sure that `TryFrom` does not trigger an ambiguous associated item error for `Error`.
#[derive(TryFrom)]
#[try_from(repr)]
#[repr(u8)]
enum EnumWithError {
Error,
}

#[test]
fn test_with_repr() {
#[derive(TryFrom, Clone, Copy, Debug, Eq, PartialEq)]
Expand Down
10 changes: 10 additions & 0 deletions tests/try_into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@ enum Foo<'lt: 'static, T: Clone, const X: usize> {
X(Wrapper<'lt, X, T>),
}

enum EnumWithError {
Error,
}

/// Making sure that `TryInto` does not trigger an ambiguous associated item error for `Error`.
#[derive(TryInto)]
enum EnumIntoEnumWithError {
Foo(EnumWithError),
}

#[test]
fn test_try_into() {
let mut i = MixedInts::SmallInt(42);
Expand Down

0 comments on commit 71df46c

Please sign in to comment.