Repository: JelteF/derive_more
Pull request #241 (2023-02-17 09:03:07)

Redesign `From` derive macro
## Synopsis

This PR is a part of replacing all attributes having `syn::Meta` syntax to custom parsing.




## Solution

Replace `#[from(types(i32, "&str"))]` with `#[from(i32, &str)]` and add support for deriving multi-field structs and enum variants with `#[from((<tuple>), (<tuple>), ...)]`.




## Checklist

- [x] Documentation is updated (if required)
- [x] Tests are added/updated (if required)
- [x] [CHANGELOG entry][l:1] is added (if required)




[l:1]: /CHANGELOG.md


ilslv: @tyranron @JelteF as I was reworking `From` attributes to accept non-string expressions, I've found some design decisions that seem suboptimal to me. 

1. Enum `#[from(forward)]` and `#[from(types)]` automatically ignores all other fields:

```rust
#[derive(From)]
enum Value {
    #[from(forward)]
    Int(i32),
    String(String),
}

// Expands to
#[automatically_derived]
impl<__FromT0> ::core::convert::From<(__FromT0)> for Value
where
    i32: ::core::convert::From<__FromT0>,
{
    #[inline]
    fn from(original: (__FromT0)) -> Value {
        Value::Int(<i32 as ::core::convert::From<__FromT0>>::from(original))
    }
}
```

```rust
#[derive(From)]
enum Value {
    #[from(types(i8))]
    Int(i32),
    String(String),
}

// Expands to
#[automatically_derived]
impl ::core::convert::From<(i32)> for Value {
    #[inline]
    fn from(original: (i32)) -> Value {
        Value::Int(original)
    }
}

#[automatically_derived]
impl ::core::convert::From<(i8)> for Value {
    #[inline]
    fn from(original: (i8)) -> Value {
        Value::Int(<i32 as ::core::convert::From<i8>>::from(original))
    }
}
```

It's possible to manually specify other fields with `#[from]` or `#[from(forward)]`, but it seems to contradict with behaviour without those attributes

```rust
#[derive(From)]
enum Value {
    #[from(forward)]
    Int(i32),
    #[from]
    String(String),
}

#[derive(From)]
enum Value {
    #[from(forward)]
    Int(i32),
    #[from(forward)]
    String(String),
}
```

2. `from(types(...))` automatically adds field type itself

```rust
#[derive(From)]
#[from(types(u8))]
struct Int(u64);

#[automatically_derived]
impl ::core::convert::From<(u64)> for Int {
    #[inline]
    fn from(original: (u64)) -> Int {
        Int(original)
    }
}

#[automatically_derived]
impl ::core::convert::From<(u8)> for Int {
    #[inline]
    fn from(original: (u8)) -> Int {
        Int(<u64 as ::core::convert::From<u8>>::from(original))
    }
}
```

3. `#[from(types(...))]` for multi-field structs automatically uses single each attribute field for all structs fields

```rust
#[derive(From)]
#[from(types(i8))]
struct Point(i32, i32);

#[automatically_derived]
impl ::core::convert::From<(i32, i32)> for Point {
    #[inline]
    fn from(original: (i32, i32)) -> Point {
        Point(original.0, original.1)
    }
}

#[automatically_derived]
impl ::core::convert::From<(i8, i8)> for Point {
    #[inline]
    fn from(original: (i8, i8)) -> Point {
        Point(
            <i32 as ::core::convert::From<i8>>::from(original.0),
            <i32 as ::core::convert::From<i8>>::from(original.1),
        )
    }
}
```
tyranron: @ilslv 

> 1. Enum `#[from(forward)]` and `#[from(types)]` automatically ignores all other fields:

`#[from(forward)]` does this, I guess, because it produces a blanket impl, which inevitably will conflict with other impls.

`#[from(types)]`, I guess, does this by mistake, because was made out of `#[from(forward)]` once.

> 2. `from(types(...))` automatically adds field type itself

This is intended behavior. Do you have better suggestions?

> 3. `#[from(types(...))]` for multi-field structs automatically uses single each attribute field for all structs fields

The same here. It was in the initial design to work with tuples.


ilslv: @tyranron 

> `#[from(types)]`, I guess, does this by mistake, because was made out of #[from(forward)] once.

👍 

> > 2. `from(types(...))` automatically adds field type itself

> This is intended behavior. Do you have better suggestions?

Explicitly including field type

> > 3. `#[from(types(...))]` for multi-field structs automatically uses single each attribute field for all structs fields

> The same here. It was in the initial design to work with tuples.

Looks very counterintuitive and niche to me, I would prefer something like: 
 
```rust
#[derive(From)]
#[from(types((i8, i8), (i16, i16)))]
struct Point(i32, i32);
```
tyranron: @ilslv 

> Explicitly including field type

This may have sense, since makes things more straightforward and allows more (excluding direct underlying type from impls).

> Looks very counterintuitive and niche to me, I would prefer something like:
> 
> ```rust
> #[derive(From)]
> #[from(types((i8, i8), (i16, i16)))]
> struct Point(i32, i32);
> ```

But here I'm not sure what it gives except making ergonomics worse.

Anyway, we need @JelteF's opinion on that.
JelteF: > 1. Enum `#[from(forward)]` and `#[from(types)]` automatically ignores all other fields:

Thinking back, I think my original reasoning was that I liked it that you could easily specify variants explicitly by specifying the `#[from]` attribute (without any arguments). The fact that `forward` almost certainly conflicts might have played a role too. I don't think the types really came in to that thought. I think I'd prefer keeping it as is. Having no attributes at all being a special case, where automatically all variants are enabled. But once you specify attributes, you should specify them all. Or you should specify a default attribute for all variants like:
```
#[derive(From)]
#[from]
enum Value {
    #[from(forward)]
    Int(i32),
    String(String),
}
```

> 2. `from(types(...))` automatically adds field type itself

I like that making it explicit you can choose not to derive the From for the actual type. But I'm strongly wondering how often that's applicable in practice. I think most people will want to derive additive for types. No-one has opened an issue so far that they couldn't do it. And maybe the better way to do so would be to allow `#[from(ignore, types(...))` to ignore the default derive.

> 3. `#[from(types(...))]` for multi-field structs automatically uses single each attribute field for all structs fields

This one I actually think makes sense to change. There's many types you currently cannot specify. And strangely it doesn't even error if you currently pass a tuple. It simply doesn't derive anything and completely ignores the tuple. One example that I'd like to be able to derive is:

```
#[derive(From)]
#[from(types(i8, &str))]
struct SomeMixedType(i32, String);
```

ilslv: @JelteF 

> And maybe the better way to do so would be to allow `#[from(ignore, types(...))` to ignore the default derive.

I think that combination of `types` and `ignore` may be quite confusing:

```rust
#[derive(From)]
enum Value {
    #[from(ignore, types(i8))]
    Int(i32),
    #[from(ignore)]
    String(String),
}
```

And even if user understands whats going on here, discovering that on their own is quite unlikely. 
tyranron: @JelteF @ilslv to sum up:

1. All agreed that `#[from(forward)]` should imply ignoring other non-marked fields.

2. All agreed on the `#[from(types((i8, i8), (i16, i16)))]` syntax for tuple structs, since it makes things less magic and allows expressing more patterns.

3. The last questions are about `#[from(types(...)]` design:
    1. Should `#[from(types(...)]` imply ignoring other non-marked fields the same way as `#[from(forward)]`?
    2. Should it implicitly imply the underlying type with disabling allowance via `ignore`, or rather implicitly disable the underlying type, requiring to specify it explicitly if necessary?
JelteF: > 3. The last questions are about `#[from(types(...)]` design:
>    1. Should `#[from(types(...)]` imply ignoring other non-marked fields the same way as `#[from(forward)]`?
>    2. Should it implicitly imply the underlying type with disabling allowance via `ignore`, or rather implicitly disable the underlying type, requiring to specify it explicitly if necessary?

For 3.i, I don't have a real strong preference either way. 
For 3.ii, I feel quite strongly that implicitly disabling the derive for the actual types seems like it's pretty much never what you want. I don't think a solution for this edge case needs to be part of this PR. If people create an issue with a usecase for it, then we can figure out some nice API then. (I agree that my `ignore` API is not really nice) But I think spending time on such an edge case usecase is not worth the effort at the moment.
tyranron: @JelteF @ilslv 

> 3.i. Should `#[from(types(...)]` imply ignoring other non-marked fields the same way as `#[from(forward)]`?

I would prefer to not have such implying. It definitely makes sense for `forward` due to the blanket impl, but for `types` it could be quite annoying during refactorings that when you add more types, some other impls disappear, while there is no real impls conflict for that to happen.

> 3.ii. Should it implicitly imply the underlying type with disabling allowance via `ignore`, or rather implicitly disable the underlying type, requiring to specify it explicitly if necessary?

I'd like to go with "implicitly disable the underlying type, requiring to specify it explicitly if necessary". Seems like a quite intuitive and unambiguous thing: either we default to the underlying type, or enumerate all the needed types explicitly.

It also frees our hands to go forward and make more simplifications like this (removing the `types`/`owned`/`ref`/`ref_mut` completely):
```rust
#[derive(From)]
enum Value {
    #[from(i8, &i32)]
    Int(i32),
    #[from(&str, &mut str, &String, String)]
    String(String),
}

#[derive(Into)]
#[into(i64, &i64, &mut i64)]
struct MyInt64(i64);
```


ilslv: @tyranron @JelteF 

> I would prefer to not have such implying. It definitely makes sense for forward due to the blanket impl, but for types it could be quite annoying during refactorings that when you add more types, some other impls disappear, while there is no real impls conflict for that to happen.

> I'd like to go with "implicitly disable the underlying type, requiring to specify it explicitly if necessary". Seems like a quite intuitive and unambiguous thing: either we default to the underlying type, or enumerate all the needed types explicitly.

I agree with both points.

> It also frees our hands to go forward and make more simplifications like this (removing the `types`/`owned`/`ref`/`ref_mut` completely):

`From` actually doesn't have any `types`/`owned`/`ref`/`ref_mut` shenanigans, only `Into` does, but agree, those attributes are annoying to me. 
tyranron: @JelteF @ilslv 

> `From` actually doesn't have any `types`/`owned`/`ref`/`ref_mut` shenanigans, only `Into` does, but agree, those attributes are annoying to me.

Yes, I've thought about it a bit more, and have concluded that we cannot actually throw `owned`/`ref`/`ref_mut` out, because they control `Self` type representation, and I see no way to auto-deduce it unambiguously.

However, nothing bad happens if we strip `types` out completely.
ilslv: @tyranron 

> > 3.i. Should `#[from(types(...)]` imply ignoring other non-marked fields the same way as `#[from(forward)]`?
> 
> I would prefer to not have such implying. It definitely makes sense for `forward` due to the blanket impl, but for `types` it could be quite annoying during refactorings that when you add more types, some other impls disappear, while there is no real impls conflict for that to happen.

Actually, I didn't realise that there is support for another feature: explicitly specifying variants to derive with explicit `#[from]`:

```rust
#[derive(Debug, From, PartialEq)]
enum Int {
    #[from]
    Derived(i32),
    NotDerived(i32),
}

// Is equivalent to:
    
#[derive(Debug, From, PartialEq)]
enum Int {
    Derived(i32),
    #[from(skip)]
    NotDerived(i32),
}
```

And from that standpoint, I think we should preserve behaviour current of skipping variants that aren't marked with `#[from(<types>)]`. 
ilslv: This PR is ready for review
JelteF: @ilslv small ping. I think this can be merged after the `types(...)` wrapper is added back (and the merge conflict in the changelog is resolved). 
JelteF: @ilslv There's a few tiny merge conflicts on this PR now that #251 is merged. Could you resolve those? 
tyranron: @JelteF I'll pick @ilslv's work up and finish it in next few days, since @ilslv is having vacations this week 🏝

But before doing so, I'd like to argue a little bit more about `types()` restoring. I want to make some points regarding the current solution which seem to be not so obvious. Don't have time to write them up today, unfortunately, but will definitely do tomorrow.
JelteF: @tyranron I'm definitely interested in this:

> But before doing so, I'd like to argue a little bit more about `types()` restoring. I want to make some points regarding the current solution which seem to be not so obvious. Don't have time to write them up today, unfortunately, but will definitely do tomorrow.

(I guess you were busy with other things, which is totally fine, but just a kind reminder)
tyranron: @JelteF yes, sorry for such delaying, and thank you for the patience!

Now, to the topic...

As you've mentioned [before](https://github.com/JelteF/derive_more/pull/241#discussion_r1141109032):
> But I don't think we should remove the `types` wrapper itself. This will make it hard for us to add new sub-attributes to the `from` attribute in a backwards compatible manner in the future, because although unlikely they might conflict with type names.

Yes, since Rust types are `PascalCased` (modulo primitives) and attributes are `snake_cased`, we very-very-very unlikely will ever have those conflicts.

But more to that (my main concern):  
Since we've changed semantics to "implicitly disable the underlying type, requiring to specify it explicitly if necessary" when the `from` attribute with `types` is specified, we leave no space, in practice, for different sub-attributes to collide. They are now mutually exclusive: either `skip`, or `forward`, or `types`, but never together.  
With this in mind, it just feels quite odd and redundant for the library user to write everywhere `#[from(types(...))]` instead of just `#[from(...)]`: why should I always write the `types(` part if there is nothing to disambiguate with?

```rust
#[derive(From)]
enum Value {
    #[from(i8, i32)]
    Int(i32),
    #[from(f32, f64)]
    Float(f64),
    #[from(MyString, String)]
    String(String),
    #[from(skip)]
    Ignored(String),
}
```

```rust
#[derive(From)]
enum Value {
    #[from(types(i8, i32))]
    Int(i32),
    #[from(types(f32, f64))]
    Float(f64),
    #[from(types(MyString, String))]
    String(String),
    #[from(skip)]
    Ignored(String),
}
```
JelteF: > Yes, since Rust types are `PascalCased` (modulo primitives) and attributes are `snake_cased`, we very-very-very unlikely will ever have those conflicts.
> 
> But more to that (my main concern): Since we've changed semantics to "implicitly disable the underlying type, requiring to specify it explicitly if necessary" when the `from` attribute with `types` is specified, we leave no space, in practice, for different sub-attributes to collide. They are now mutually exclusive: either `skip`, or `forward`, or `types`, but never together. With this in mind, it just feels quite odd and redundant for the library user to write everywhere `#[from(types(...))]` instead of just `#[from(...)]`: why should I always write the `types(` part if there is nothing to disambiguate with?

Fair enough. Let's do it it without the types wrapper.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0332d27..10ca069 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -31,6 +31,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 - The `From` derive doesn't derive `From<()>` for enum variants without any
   fields anymore. This feature was removed because it was considered useless in
   practice.
+- The `From` derive now uses `#[from(<types>)]` instead of `#[from(types(<types>))]`
+  and ignores field type itself.
 
 ### Added
 
diff --git a/Cargo.toml b/Cargo.toml
index 7ad2a17..deede6e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -33,6 +33,7 @@ rustc_version = { version = "0.4", optional = true }
 
 [dev-dependencies]
 rustversion = "1.0"
+static_assertions = "1.1"
 trybuild = "1.0.56"
 
 [badges]
@@ -229,7 +230,7 @@ required-features = ["try_unwrap"]
 [[test]]
 name = "compile_fail"
 path = "tests/compile_fail/mod.rs"
-required-features = ["debug", "display"]
+required-features = ["debug", "display", "from"]
 
 [[test]]
 name = "no_std"
diff --git a/impl/doc/from.md b/impl/doc/from.md
index d0ab8f6..1bc0cb4 100644
--- a/impl/doc/from.md
+++ b/impl/doc/from.md
@@ -2,263 +2,187 @@
 
 The point of deriving this type is that it makes it easy to create a new
 instance of the type by using the `.into()` method on the value(s) that it
-should contain.
-This is done by implementing the `From` trait for the type that is passed to the
-derive.
-For structs with a single field you can call `.into()` on the desired content
-itself after deriving `From`.
-For structs that have multiple fields `.into()` needs to be called on a tuple
-containing the desired content for each field.
-For enums `.into()` works for each variant as if they were structs.
-This way the variant can not only be initialized, but also be chosen based on
-the type that `.into()` is called on.
+should contain. This is done by implementing the `From` trait for the type
+that is passed to the derive.
 
 
 
 
-## Example usage
+## Structs
+
+For structs with a single field you can call `.into()` on the desired content
+itself after deriving `From`.
 
 ```rust
 # use derive_more::From;
 #
-// Allow converting from i32
-#[derive(From, PartialEq)]
-struct MyInt(i32);
+#[derive(Debug, From, PartialEq)]
+struct Int(i32);
 
-// Forward from call to the field, so allow converting
-// from anything that can be converted into an i64 (so most integers)
-#[derive(From, PartialEq)]
-#[from(forward)]
-struct MyInt64(i64);
-
-// You can ignore a variant
-#[derive(From, PartialEq)]
-enum MyEnum {
-    SmallInt(i32),
-    NamedBigInt { int: i64 },
-    #[from(ignore)]
-    NoFromImpl(i64),
-}
-
-// Or explicitly annotate the ones you need
-#[derive(From, PartialEq)]
-enum MyEnum2 {
-    #[from]
-    SmallInt(i32),
-    #[from]
-    NamedBigInt { int: i64 },
-    NoFromImpl(i64),
-}
-
-// And even specify additional conversions for them
-#[derive(From, PartialEq)]
-enum MyEnum3 {
-    #[from(types(i8))]
-    SmallInt(i32),
-    #[from(types(i16))]
-    NamedBigInt { int: i64 },
-    NoFromImpl(i64),
-}
-
-assert!(MyInt(2) == 2.into());
-assert!(MyInt64(6) == 6u8.into());
-assert!(MyEnum::SmallInt(123) == 123i32.into());
-assert!(MyEnum::SmallInt(123) != 123i64.into());
-assert!(MyEnum::NamedBigInt{int: 123} == 123i64.into());
-assert!(MyEnum3::SmallInt(123) == 123i8.into());
-assert!(MyEnum3::NamedBigInt{int: 123} == 123i16.into());
+assert_eq!(Int(2), 2.into());
 ```
 
+For structs that have multiple fields `.into()` needs to be called on a tuple
+containing the desired content for each field.
 
+```rust
+# use derive_more::From;
+#
+#[derive(Debug, From, PartialEq)]
+struct Point(i32, i32);
 
+assert_eq!(Point(1, 2), (1, 2).into());
+```
 
-## Tuple structs
-
-When deriving for a tuple struct with a single field (i.e. a newtype) like this:
+To specify concrete types to derive convert from use `#[from(<types>)]`.
 
 ```rust
+# use std::borrow::Cow;
+#
 # use derive_more::From;
 #
-#[derive(From)]
-struct MyInt(i32);
-```
+#[derive(Debug, From, PartialEq)]
+#[from(Cow<'static, str>, String, &'static str)]
+struct Str(Cow<'static, str>);
 
-Code like this will be generated:
+assert_eq!(Str("&str".into()), "&str".into());
+assert_eq!(Str("String".into()), "String".to_owned().into());
+assert_eq!(Str("Cow".into()), Cow::Borrowed("Cow").to_owned().into());
 
-```rust
-# struct MyInt(i32);
-impl ::core::convert::From<(i32)> for MyInt {
-    fn from(original: (i32)) -> MyInt {
-        MyInt(original)
-    }
+#[derive(Debug, From, PartialEq)]
+#[from((i16, i16), (i32, i32))]
+struct Point {
+    x: i32,
+    y: i32,
 }
+
+assert_eq!(Point { x: 1_i32, y: 2_i32 }, (1_i16, 2_i16).into());
+assert_eq!(Point { x: 3_i32, y: 4_i32 }, (3_i32, 4_i32).into());
 ```
 
-The behaviour is a bit different when deriving for a struct with multiple
-fields. For instance when deriving for a tuple struct with two fields like this:
+Also, you can forward implementation to the inner type, which means deriving `From` for any type, that derives `From`
+inner type.
 
 ```rust
+# use std::borrow::Cow;
+#
 # use derive_more::From;
 #
-#[derive(From)]
-struct MyInts(i32, i32);
-```
-
-Code like this will be generated:
-
-```rust
-# struct MyInts(i32, i32);
-impl ::core::convert::From<(i32, i32)> for MyInts {
-    fn from(original: (i32, i32)) -> MyInts {
-        MyInts(original.0, original.1)
-    }
+#[derive(Debug, From, PartialEq)]
+#[from(forward)]
+struct Str {
+    inner: Cow<'static, str>,
 }
+
+assert_eq!(Str { inner: "&str".into() }, "&str".into());
+assert_eq!(Str { inner: "String".into() }, "String".to_owned().into());
+assert_eq!(Str { inner: "Cow".into() }, Cow::Borrowed("Cow").to_owned().into());
 ```
 
 
 
 
-## Regular structs
+## Enums
 
-For regular structs almost the same code is generated as for tuple structs
-except in the way the field values are assigned to the new struct.
-When deriving for a regular struct with a single field like this:
+For enums `.into()` works for each variant as if they were structs. This
+includes specifying concrete types via `#[from(<types>)]` or forwarding
+implementation with `#[from(forward)]`.
 
 ```rust
 # use derive_more::From;
 #
-#[derive(From)]
-struct Point1D {
-    x: i32,
+#[derive(Debug, From, PartialEq)]
+enum IntOrPoint {
+    Int(i32),
+    Point {
+        x: i32,
+        y: i32,
+    },
 }
-```
-
-Code like this will be generated:
 
-```rust
-# struct Point1D {
-#     x: i32,
-# }
-impl ::core::convert::From<(i32)> for Point1D {
-    fn from(original: (i32)) -> Point1D {
-        Point1D { x: original }
-    }
-}
+assert_eq!(IntOrPoint::Int(1), 1.into());
+assert_eq!(IntOrPoint::Point { x: 1, y: 2 }, (1, 2).into());
 ```
 
-The behaviour is a bit different when deriving for a struct with multiple
-fields. For instance when deriving for a tuple struct with two fields like this:
+By default, `From` is generated for every enum variant, but you can skip some
+variants via `#[from(skip)]` or only concrete fields via `#[from]`.
 
 ```rust
+# mod from {
 # use derive_more::From;
-#
-#[derive(From)]
-struct Point2D {
-    x: i32,
-    y: i32,
+#[derive(Debug, From, PartialEq)]
+enum Int {
+    #[from]
+    Derived(i32),
+    NotDerived(i32),
 }
-```
+# }
 
-Code like this will be generated:
+// Is equivalent to:
 
-```rust
-# struct Point2D {
-#     x: i32,
-#     y: i32,
-# }
-impl ::core::convert::From<(i32, i32)> for Point2D {
-    fn from(original: (i32, i32)) -> Point2D {
-        Point2D {
-            x: original.0,
-            y: original.1,
-        }
-    }
+# mod skip {
+# use derive_more::From;
+#[derive(Debug, From, PartialEq)]
+enum Int {
+    Derived(i32),
+    #[from(skip)]
+    NotDerived(i32),
 }
+# }
 ```
 
 
 
 
-## Enums
-
-When deriving `From` for enums a new `impl` will be generated for each of its
-variants.
-If you don't want this for a variant you can put the `#[from(ignore)]` attribute
-on that variant. One case where this can be useful is when two variants would
-overlap.
-For instance when deriving `From` for the following enum:
+## Example usage
 
 ```rust
 # use derive_more::From;
 #
-#[derive(From)]
-enum MixedInts {
+// Allow converting from i32
+#[derive(From, PartialEq)]
+struct MyInt(i32);
+
+// Forward from call to the field, so allow converting
+// from anything that can be converted into an i64 (so most integers)
+#[derive(From, PartialEq)]
+#[from(forward)]
+struct MyInt64(i64);
+
+// You can ignore a variant
+#[derive(From, PartialEq)]
+enum MyEnum {
     SmallInt(i32),
     NamedBigInt { int: i64 },
-    TwoSmallInts(i32, i32),
-    NamedBigInts { x: i64, y: i64 },
     #[from(ignore)]
-    Unsigned(u32),
-    NamedUnsigned { x: u32 },
-}
-```
-
-Code like this will be generated:
-
-```rust
-# enum MixedInts {
-#     SmallInt(i32),
-#     NamedBigInt { int: i64 },
-#     TwoSmallInts(i32, i32),
-#     NamedBigInts { x: i64, y: i64 },
-#     Unsigned(u32),
-#     NamedUnsigned { x: u32 },
-# }
-impl ::core::convert::From<(i32)> for MixedInts {
-    #[allow(unused_variables)]
-    #[inline]
-    fn from(original: (i32)) -> MixedInts {
-        MixedInts::SmallInt(original)
-    }
-}
-
-impl ::core::convert::From<(i64)> for MixedInts {
-    #[allow(unused_variables)]
-    #[inline]
-    fn from(original: (i64)) -> MixedInts {
-        MixedInts::NamedBigInt { int: original }
-    }
+    NoFromImpl(i64),
 }
 
-impl ::core::convert::From<(i32, i32)> for MixedInts {
-    #[allow(unused_variables)]
-    #[inline]
-    fn from(original: (i32, i32)) -> MixedInts {
-        MixedInts::TwoSmallInts(original.0, original.1)
-    }
+// Or explicitly annotate the ones you need
+#[derive(From, PartialEq)]
+enum MyEnum2 {
+    #[from]
+    SmallInt(i32),
+    #[from]
+    NamedBigInt { int: i64 },
+    NoFromImpl(i64),
 }
 
-impl ::core::convert::From<(i64, i64)> for MixedInts {
-    #[allow(unused_variables)]
-    #[inline]
-    fn from(original: (i64, i64)) -> MixedInts {
-        MixedInts::NamedBigInts {
-            x: original.0,
-            y: original.1,
-        }
-    }
+// And even specify additional conversions for them
+#[derive(From, PartialEq)]
+enum MyEnum3 {
+    #[from(i8, i32)]
+    SmallInt(i32),
+    #[from(i16, i64)]
+    NamedBigInt { int: i64 },
+    NoFromImpl(i64),
 }
 
-impl ::core::convert::From<(u32)> for MixedInts {
-    #[allow(unused_variables)]
-    #[inline]
-    fn from(original: (u32)) -> MixedInts {
-        MixedInts::NamedUnsigned { x: original }
-    }
-}
+assert!(MyInt(2) == 2.into());
+assert!(MyInt64(6) == 6u8.into());
+assert!(MyEnum::SmallInt(123) == 123i32.into());
+assert!(MyEnum::SmallInt(123) != 123i64.into());
+assert!(MyEnum::NamedBigInt{int: 123} == 123i64.into());
+assert!(MyEnum3::SmallInt(123) == 123i8.into());
+assert!(MyEnum3::NamedBigInt{int: 123} == 123i16.into());
 ```
-
-Without the `#[from(ignore)]` on `Unsigned`, no `impl` would be generated for
-`Unsigned` and `NamedUnsigned`. The reason for this is that it would be
-impossible for the compiler to know which implementation to choose, since they
-would both implement `From<u32>`.
diff --git a/impl/src/fmt/display.rs b/impl/src/fmt/display.rs
index 7eb893e..ff8f928 100644
--- a/impl/src/fmt/display.rs
+++ b/impl/src/fmt/display.rs
@@ -9,7 +9,6 @@ use syn::{
     parse::{Parse, ParseStream},
     parse_quote,
     spanned::Spanned as _,
-    Error, Result,
 };
 
 use super::{BoundsAttribute, FmtAttribute};
@@ -25,7 +24,7 @@ use super::{BoundsAttribute, FmtAttribute};
 /// - [`Pointer`](fmt::Pointer)
 /// - [`UpperExp`](fmt::UpperExp)
 /// - [`UpperHex`](fmt::UpperHex)
-pub fn expand(input: &syn::DeriveInput, trait_name: &str) -> Result<TokenStream> {
+pub fn expand(input: &syn::DeriveInput, trait_name: &str) -> syn::Result<TokenStream> {
     let trait_name = normalize_trait_name(trait_name);
 
     let attrs = Attributes::parse_attrs(&input.attrs, trait_name)?;
@@ -73,7 +72,7 @@ type ExpansionCtx<'a> = (&'a Attributes, &'a Ident, &'a Ident, &'a str);
 fn expand_struct(
     s: &syn::DataStruct,
     (attrs, ident, trait_ident, _): ExpansionCtx<'_>,
-) -> Result<(Vec<syn::WherePredicate>, TokenStream)> {
+) -> syn::Result<(Vec<syn::WherePredicate>, TokenStream)> {
     let s = Expansion {
         attrs,
         fields: &s.fields,
@@ -106,7 +105,7 @@ fn expand_struct(
 fn expand_enum(
     e: &syn::DataEnum,
     (attrs, _, trait_ident, trait_name): ExpansionCtx<'_>,
-) -> Result<(Vec<syn::WherePredicate>, TokenStream)> {
+) -> syn::Result<(Vec<syn::WherePredicate>, TokenStream)> {
     if attrs.fmt.is_some() {
         todo!("https://github.com/JelteF/derive_more/issues/142");
     }
@@ -121,7 +120,7 @@ fn expand_enum(
                 && variant.fields.is_empty()
                 && trait_name != "Display"
             {
-                return Err(Error::new(
+                return Err(syn::Error::new(
                     e.variants.span(),
                     format!(
                         "implicit formatting of unit enum variant is supported \
@@ -157,7 +156,7 @@ fn expand_enum(
 
             arms.extend([quote! { #matcher => { #arm_body }, }]);
 
-            Ok::<_, Error>((bounds, arms))
+            Ok::<_, syn::Error>((bounds, arms))
         },
     )?;
 
@@ -173,9 +172,9 @@ fn expand_enum(
 fn expand_union(
     u: &syn::DataUnion,
     (attrs, _, _, trait_name): ExpansionCtx<'_>,
-) -> Result<(Vec<syn::WherePredicate>, TokenStream)> {
+) -> syn::Result<(Vec<syn::WherePredicate>, TokenStream)> {
     let fmt = &attrs.fmt.as_ref().ok_or_else(|| {
-        Error::new(
+        syn::Error::new(
             u.fields.span(),
             format!(
                 "unions must have `#[{}(\"...\", ...)]` attribute",
@@ -183,13 +182,11 @@ fn expand_union(
             ),
         )
     })?;
-    let (lit, args) = (&fmt.lit, &fmt.args);
 
-    let body = quote! {
-        ::core::write!(__derive_more_f, #lit, #( #args ),*)
-    };
-
-    Ok((attrs.bounds.0.clone().into_iter().collect(), body))
+    Ok((
+        attrs.bounds.0.clone().into_iter().collect(),
+        quote! { ::core::write!(__derive_more_f, #fmt) },
+    ))
 }
 
 /// Representation of a [`fmt::Display`]-like derive macro attribute.
@@ -215,7 +212,7 @@ impl Attributes {
     fn parse_attrs(
         attrs: impl AsRef<[syn::Attribute]>,
         trait_name: &str,
-    ) -> Result<Self> {
+    ) -> syn::Result<Self> {
         attrs
             .as_ref()
             .iter()
@@ -227,10 +224,10 @@ impl Attributes {
                         attrs.bounds.0.extend(more.0);
                     }
                     Attribute::Fmt(fmt) => {
-                        attrs.fmt.replace(fmt).map_or(Ok(()), |dup| Err(Error::new(
+                        attrs.fmt.replace(fmt).map_or(Ok(()), |dup| Err(syn::Error::new(
                             dup.span(),
                             format!(
-                                "Multiple `#[{}(\"...\", ...)]` attributes aren't allowed",
+                                "multiple `#[{}(\"...\", ...)]` attributes aren't allowed",
                                 trait_name_to_attribute_name(trait_name),
                             ))))?;
                     }
@@ -251,7 +248,7 @@ enum Attribute {
 }
 
 impl Parse for Attribute {
-    fn parse(input: ParseStream<'_>) -> Result<Self> {
+    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
         BoundsAttribute::check_legacy_fmt(input)?;
         FmtAttribute::check_legacy_fmt(input)?;
 
@@ -291,19 +288,12 @@ impl<'a> Expansion<'a> {
     /// greater than 1.
     ///
     /// [`Display::fmt()`]: fmt::Display::fmt()
-    fn generate_body(&self) -> Result<TokenStream> {
+    fn generate_body(&self) -> syn::Result<TokenStream> {
         match &self.attrs.fmt {
-            Some(fmt) => {
-                let (lit, args) = (&fmt.lit, &fmt.args);
-                Ok(quote! {
-                    ::core::write!(__derive_more_f, #lit, #( #args ),*)
-                })
-            }
+            Some(fmt) => Ok(quote! { ::core::write!(__derive_more_f, #fmt) }),
             None if self.fields.is_empty() => {
                 let ident_str = self.ident.to_string();
-                Ok(quote! {
-                    ::core::write!(__derive_more_f, #ident_str)
-                })
+                Ok(quote! { ::core::write!(__derive_more_f, #ident_str) })
             }
             None if self.fields.len() == 1 => {
                 let field = self
@@ -317,7 +307,7 @@ impl<'a> Expansion<'a> {
                     ::core::fmt::#trait_ident::fmt(#ident, __derive_more_f)
                 })
             }
-            _ => Err(Error::new(
+            _ => Err(syn::Error::new(
                 self.fields.span(),
                 format!(
                     "struct or enum variant with more than 1 field must have \
diff --git a/impl/src/fmt/mod.rs b/impl/src/fmt/mod.rs
index 41e5754..5727d67 100644
--- a/impl/src/fmt/mod.rs
+++ b/impl/src/fmt/mod.rs
@@ -8,25 +8,24 @@ pub(crate) mod debug;
 pub(crate) mod display;
 mod parsing;
 
-use std::{iter, mem};
-
-use proc_macro2::{Ident, TokenStream, TokenTree};
+use proc_macro2::TokenStream;
 use quote::ToTokens;
 use syn::{
-    buffer::Cursor,
     parse::{Parse, ParseStream},
     punctuated::Punctuated,
     spanned::Spanned as _,
-    token, Error, Result,
+    token, Ident,
 };
 
+use crate::parsing::Expr;
+
 /// Representation of a macro attribute expressing additional trait bounds.
 #[derive(Debug, Default)]
 struct BoundsAttribute(Punctuated<syn::WherePredicate, syn::token::Comma>);
 
 impl BoundsAttribute {
     /// Errors in case legacy syntax is encountered: `bound = "..."`.
-    fn check_legacy_fmt(input: ParseStream<'_>) -> Result<()> {
+    fn check_legacy_fmt(input: ParseStream<'_>) -> syn::Result<()> {
         let fork = input.fork();
 
         let path = fork
@@ -41,7 +40,7 @@ impl BoundsAttribute {
                     _ => None,
                 })
                 .map_or(Ok(()), |bound| {
-                    Err(Error::new(
+                    Err(syn::Error::new(
                         input.span(),
                         format!("legacy syntax, use `bound({bound})` instead"),
                     ))
@@ -52,7 +51,7 @@ impl BoundsAttribute {
 }
 
 impl Parse for BoundsAttribute {
-    fn parse(input: ParseStream<'_>) -> Result<Self> {
+    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
         let _ = input.parse::<syn::Path>().and_then(|p| {
             if ["bound", "bounds", "where"]
                 .into_iter()
@@ -60,7 +59,7 @@ impl Parse for BoundsAttribute {
             {
                 Ok(p)
             } else {
-                Err(Error::new(
+                Err(syn::Error::new(
                     p.span(),
                     "unknown attribute, expected `bound(...)`",
                 ))
@@ -86,8 +85,11 @@ struct FmtAttribute {
     /// [`syn::LitStr`]: struct@syn::LitStr
     lit: syn::LitStr,
 
+    /// Optional [`token::Comma`].
+    comma: Option<token::Comma>,
+
     /// Interpolation arguments.
-    args: Vec<FmtArgument>,
+    args: Punctuated<FmtArgument, token::Comma>,
 }
 
 impl FmtAttribute {
@@ -104,11 +106,12 @@ impl FmtAttribute {
                 Parameter::Named(name) => self
                     .args
                     .iter()
-                    .find_map(|a| (a.alias.as_ref()? == &name).then_some(&a.expr))
+                    .find_map(|a| (a.alias()? == &name).then_some(&a.expr))
                     .map_or(Some(name), |expr| expr.ident().map(ToString::to_string))?,
                 Parameter::Positional(i) => self
                     .args
-                    .get(i)
+                    .iter()
+                    .nth(i)
                     .and_then(|a| a.expr.ident().filter(|_| a.alias.is_none()))?
                     .to_string(),
             };
@@ -129,7 +132,7 @@ impl FmtAttribute {
     }
 
     /// Errors in case legacy syntax is encountered: `fmt = "...", (arg),*`.
-    fn check_legacy_fmt(input: ParseStream<'_>) -> Result<()> {
+    fn check_legacy_fmt(input: ParseStream<'_>) -> syn::Result<()> {
         let fork = input.fork();
 
         let path = fork
@@ -154,7 +157,7 @@ impl FmtAttribute {
                 (!args.is_empty()).then_some(args)
             })()
             .map_or(Ok(()), |fmt| {
-                Err(Error::new(
+                Err(syn::Error::new(
                     input.span(),
                     format!(
                         "legacy syntax, remove `fmt =` and use `{}` instead",
@@ -168,28 +171,14 @@ impl FmtAttribute {
 }
 
 impl Parse for FmtAttribute {
-    fn parse(input: ParseStream) -> Result<Self> {
-        let fmt_lit = input.parse()?;
-
-        if input.peek(syn::token::Comma) {
-            let _ = input.parse::<syn::token::Comma>()?;
-        }
-
-        let fmt_args = input.step(|cursor| {
-            let mut cursor = *cursor;
-            let arguments = iter::from_fn(|| {
-                let (arg, c) = FmtArgument::parse(cursor)?;
-                cursor = c;
-                Some(arg)
-            })
-            .collect();
-
-            Ok((arguments, cursor))
-        })?;
-
+    fn parse(input: ParseStream) -> syn::Result<Self> {
         Ok(Self {
-            lit: fmt_lit,
-            args: fmt_args,
+            lit: input.parse()?,
+            comma: input
+                .peek(syn::token::Comma)
+                .then(|| input.parse())
+                .transpose()?,
+            args: input.parse_terminated(FmtArgument::parse, token::Comma)?,
         })
     }
 }
@@ -197,10 +186,8 @@ impl Parse for FmtAttribute {
 impl ToTokens for FmtAttribute {
     fn to_tokens(&self, tokens: &mut TokenStream) {
         self.lit.to_tokens(tokens);
-        for arg in &self.args {
-            syn::token::Comma(arg.span()).to_tokens(tokens);
-            arg.to_tokens(tokens);
-        }
+        self.comma.to_tokens(tokens);
+        self.args.to_tokens(tokens);
     }
 }
 
@@ -208,252 +195,45 @@ impl ToTokens for FmtAttribute {
 /// in a [`FmtAttribute`].
 ///
 /// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#named-parameters
-#[derive(Debug, Default)]
+#[derive(Debug)]
 struct FmtArgument {
-    /// `identifier` [`Ident`].
-    alias: Option<Ident>,
+    /// `identifier =` [`Ident`].
+    alias: Option<(Ident, token::Eq)>,
 
-    /// `expression` [`FmtExpr`].
-    expr: FmtExpr,
+    /// `expression` [`Expr`].
+    expr: Expr,
 }
 
 impl FmtArgument {
-    /// Parses a [`FmtArgument`] and returns a new [`Cursor`], where parsing
-    /// can be continued.
+    /// Returns an `identifier` of the [named parameter][1].
     ///
-    /// Returns [`None`] in case of eof or trailing comma.
-    fn parse(mut cursor: Cursor<'_>) -> Option<(FmtArgument, Cursor<'_>)> {
-        let mut arg = FmtArgument::default();
-
-        if let Some((ident, c)) = cursor.ident() {
-            if let Some((_eq, c)) = c.punct().filter(|(p, _)| p.as_char() == '=') {
-                arg.alias = Some(ident);
-                cursor = c;
-            }
-        }
-
-        if let Some((ident, c)) = cursor.ident() {
-            if let Some(c) = c
-                .punct()
-                .and_then(|(p, c)| (p.as_char() == ',').then_some(c))
-                .or_else(|| c.eof().then_some(c))
-            {
-                arg.expr = FmtExpr::Ident(ident);
-                return Some((arg, c));
-            }
-        }
-
-        let (rest, c) = Self::parse_rest(cursor);
-        arg.expr.extend(rest);
-
-        (!arg.expr.is_empty()).then_some((arg, c))
-    }
-
-    /// Parses the rest, until the end of a [`FmtArgument`] (comma or eof),
-    /// in case simplest case of `(ident =)? ident(,|eof)` wasn't parsed.
-    fn parse_rest(mut cursor: Cursor<'_>) -> (TokenStream, Cursor<'_>) {
-        let mut out = TokenStream::new();
-
-        loop {
-            if let Some(extend) = Self::turbofish(cursor) {
-                cursor = extend(&mut out);
-                continue;
-            }
-            if let Some(extend) = Self::closure_args(cursor) {
-                cursor = extend(&mut out);
-                continue;
-            }
-            if let Some(extend) = Self::qself(cursor) {
-                cursor = extend(&mut out);
-                continue;
-            }
-
-            if let Some(c) = cursor
-                .punct()
-                .and_then(|(p, c)| (p.as_char() == ',').then_some(c))
-                .or_else(|| cursor.eof().then_some(cursor))
-            {
-                return (out, c);
-            }
-
-            let (tt, c) = cursor
-                .token_tree()
-                .unwrap_or_else(|| unreachable!("checked for eof"));
-            out.extend([tt]);
-            cursor = c;
-        }
-    }
-
-    /// Tries to parse `| (closure_arg),* |`.
-    fn closure_args<'a>(
-        cursor: Cursor<'a>,
-    ) -> Option<impl FnOnce(&mut TokenStream) -> Cursor<'a> + 'a> {
-        let (open, c) = cursor.punct().filter(|(p, _)| p.as_char() == '|')?;
-
-        Some(move |stream: &mut TokenStream| {
-            stream.extend([TokenTree::Punct(open)]);
-            // We can ignore inner `|`, because only other place it can appear
-            // is in or pattern (ex. `Either::Left(v) | Either::Right(v)`),
-            // which must be parenthesized, so will be parsed as one
-            // `TokenTree`.
-            let (more, c) = Self::parse_until_closing('|', '|', c);
-            stream.extend(more);
-            c
-        })
-    }
-
-    /// Tries to parse `::< ... >`.
-    fn turbofish<'a>(
-        cursor: Cursor<'a>,
-    ) -> Option<impl FnOnce(&mut TokenStream) -> Cursor<'a> + 'a> {
-        use proc_macro2::Spacing;
-
-        let (colon1, c) = cursor
-            .punct()
-            .filter(|(p, _)| p.as_char() == ':' && p.spacing() == Spacing::Joint)?;
-        let (colon2, c) = c.punct().filter(|(p, _)| p.as_char() == ':')?;
-        let (less, c) = c.punct().filter(|(p, _)| p.as_char() == '<')?;
-
-        Some(move |stream: &mut TokenStream| {
-            stream.extend([colon1, colon2, less].map(TokenTree::Punct));
-            let (more, c) = Self::parse_until_closing('<', '>', c);
-            stream.extend(more);
-            c
-        })
+    /// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#named-parameters
+    fn alias(&self) -> Option<&Ident> {
+        self.alias.as_ref().map(|(ident, _)| ident)
     }
+}
 
-    /// Tries to parse `< ... as ... >::`.
-    fn qself<'a>(
-        cursor: Cursor<'a>,
-    ) -> Option<impl FnOnce(&mut TokenStream) -> Cursor<'a> + 'a> {
-        use proc_macro2::Spacing;
-
-        let (less, c) = cursor.punct().filter(|(p, _)| p.as_char() == '<')?;
-        let (more, c) = Self::parse_until_closing('<', '>', c);
-        let (colon1, c) = c
-            .punct()
-            .filter(|(p, _)| p.as_char() == ':' && p.spacing() == Spacing::Joint)?;
-        let (colon2, c) = c.punct().filter(|(p, _)| p.as_char() == ':')?;
-
-        Some(move |stream: &mut TokenStream| {
-            stream.extend([less].map(TokenTree::Punct));
-            stream.extend(more);
-            stream.extend([colon1, colon2].map(TokenTree::Punct));
-            c
+impl Parse for FmtArgument {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        Ok(Self {
+            alias: (input.peek(Ident) && input.peek2(token::Eq))
+                .then(|| Ok::<_, syn::Error>((input.parse()?, input.parse()?)))
+                .transpose()?,
+            expr: input.parse()?,
         })
     }
-
-    /// Parses until balanced amount of `open` and `close` [`TokenTree::Punct`]
-    /// or eof.
-    ///
-    /// [`Cursor`] should be pointing **right after** the first `open`ing.
-    fn parse_until_closing(
-        open: char,
-        close: char,
-        mut cursor: Cursor<'_>,
-    ) -> (TokenStream, Cursor<'_>) {
-        let mut out = TokenStream::new();
-        let mut count = 1;
-
-        while let Some((tt, c)) = cursor.token_tree().filter(|_| count != 0) {
-            match tt {
-                TokenTree::Punct(ref p) if p.as_char() == close => {
-                    count -= 1;
-                }
-                TokenTree::Punct(ref p) if p.as_char() == open => {
-                    count += 1;
-                }
-                _ => {}
-            }
-
-            out.extend([tt]);
-            cursor = c;
-        }
-
-        (out, cursor)
-    }
 }
 
 impl ToTokens for FmtArgument {
     fn to_tokens(&self, tokens: &mut TokenStream) {
-        if let Some(alias) = &self.alias {
-            alias.to_tokens(tokens);
-            syn::token::Eq::default().to_tokens(tokens);
+        if let Some((ident, eq)) = &self.alias {
+            ident.to_tokens(tokens);
+            eq.to_tokens(tokens);
         }
         self.expr.to_tokens(tokens);
     }
 }
 
-/// Representation of an expression being a [`FmtArgument`].
-///
-/// **NOTE**: This type is used instead of a [`syn::Expr`] to avoid using
-///           [`syn`]'s `full` feature significantly blowing up compilation times.
-#[derive(Debug)]
-enum FmtExpr {
-    /// [`Ident`].
-    Ident(Ident),
-
-    /// Plain [`TokenStream`].
-    TokenStream(TokenStream),
-}
-
-impl FmtExpr {
-    /// Returns an [`Ident`] in case this [`FmtExpr`] contains only it, or [`None`]
-    /// otherwise.
-    fn ident(&self) -> Option<&Ident> {
-        match self {
-            Self::Ident(i) => Some(i),
-            Self::TokenStream(_) => None,
-        }
-    }
-
-    /// Checks whether this [`FmtExpr`] is empty.
-    fn is_empty(&self) -> bool {
-        match self {
-            Self::Ident(_) => false,
-            Self::TokenStream(stream) => stream.is_empty(),
-        }
-    }
-}
-
-impl Default for FmtExpr {
-    fn default() -> Self {
-        Self::TokenStream(TokenStream::new())
-    }
-}
-
-impl ToTokens for FmtExpr {
-    fn to_tokens(&self, tokens: &mut TokenStream) {
-        match self {
-            Self::Ident(ident) => ident.to_tokens(tokens),
-            Self::TokenStream(ts) => ts.to_tokens(tokens),
-        }
-    }
-}
-
-impl Extend<TokenTree> for FmtExpr {
-    fn extend<T: IntoIterator<Item = TokenTree>>(&mut self, iter: T) {
-        *self = iter
-            .into_iter()
-            .fold(mem::take(self), |this, tt| match (this, tt) {
-                (Self::TokenStream(stream), TokenTree::Ident(ident))
-                    if stream.is_empty() =>
-                {
-                    Self::Ident(ident)
-                }
-                (Self::TokenStream(mut stream), tt) => {
-                    stream.extend([tt]);
-                    Self::TokenStream(stream)
-                }
-                (Self::Ident(ident), tt) => {
-                    let mut stream = ident.into_token_stream();
-                    stream.extend([tt]);
-                    Self::TokenStream(stream)
-                }
-            });
-    }
-}
-
 /// Representation of a [parameter][1] used in a [`Placeholder`].
 ///
 /// [1]: https://doc.rust-lang.org/stable/std/fmt/index.html#formatting-parameters
diff --git a/impl/src/from.rs b/impl/src/from.rs
index 1896167..4397f94 100644
--- a/impl/src/from.rs
+++ b/impl/src/from.rs
@@ -1,128 +1,583 @@
-use std::iter;
+//! Implementation of a [`From`] derive macro.
 
-use proc_macro2::TokenStream;
-use quote::{format_ident, quote, ToTokens};
-use syn::{parse::Result, DeriveInput, Index};
+use std::{cmp, iter};
 
-use crate::utils::{
-    add_where_clauses_for_new_ident, AttrParams, DeriveType, HashMap, MultiFieldData,
-    RefType, State,
+use proc_macro2::{Span, TokenStream};
+use quote::{format_ident, quote, ToTokens as _, TokenStreamExt as _};
+use syn::{
+    parse::{discouraged::Speculative as _, Parse, ParseStream},
+    parse_quote,
+    punctuated::Punctuated,
+    spanned::Spanned as _,
+    token, Ident,
 };
 
-/// Provides the hook to expand `#[derive(From)]` into an implementation of `From`
-pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
-    let state = State::with_attr_params(
-        input,
-        trait_name,
-        quote! { ::core::convert },
-        trait_name.to_lowercase(),
-        AttrParams {
-            enum_: vec!["forward", "ignore"],
-            variant: vec!["forward", "ignore", "types"],
-            struct_: vec!["forward", "types"],
-            field: vec!["forward"],
-        },
-    )?;
-    if state.derive_type == DeriveType::Enum {
-        Ok(enum_from(input, state))
-    } else {
-        Ok(struct_from(input, &state))
+use crate::{parsing::Type, utils::polyfill};
+
+/// Expands a [`From`] derive macro.
+pub fn expand(input: &syn::DeriveInput, _: &'static str) -> syn::Result<TokenStream> {
+    match &input.data {
+        syn::Data::Struct(data) => Expansion {
+            attrs: StructAttribute::parse_attrs(&input.attrs, &data.fields)?
+                .map(Into::into)
+                .as_ref(),
+            ident: &input.ident,
+            variant: None,
+            fields: &data.fields,
+            generics: &input.generics,
+            has_explicit_from: false,
+        }
+        .expand(),
+        syn::Data::Enum(data) => {
+            let mut has_explicit_from = false;
+            let attrs = data
+                .variants
+                .iter()
+                .map(|variant| {
+                    let attrs =
+                        VariantAttribute::parse_attrs(&variant.attrs, &variant.fields)?;
+                    if matches!(
+                        attrs,
+                        Some(
+                            VariantAttribute::From
+                                | VariantAttribute::Types(_)
+                                | VariantAttribute::Forward
+                        ),
+                    ) {
+                        has_explicit_from = true;
+                    }
+                    Ok(attrs)
+                })
+                .collect::<syn::Result<Vec<_>>>()?;
+
+            data.variants
+                .iter()
+                .zip(&attrs)
+                .map(|(variant, attrs)| {
+                    Expansion {
+                        attrs: attrs.as_ref(),
+                        ident: &input.ident,
+                        variant: Some(&variant.ident),
+                        fields: &variant.fields,
+                        generics: &input.generics,
+                        has_explicit_from,
+                    }
+                    .expand()
+                })
+                .collect()
+        }
+        syn::Data::Union(data) => Err(syn::Error::new(
+            data.union_token.span(),
+            "`From` cannot be derived for unions",
+        )),
     }
 }
 
-pub fn struct_from(input: &DeriveInput, state: &State) -> TokenStream {
-    let multi_field_data = state.enabled_fields_data();
-    let MultiFieldData {
-        fields,
-        variant_info,
-        infos,
-        input_type,
-        trait_path,
-        ..
-    } = multi_field_data.clone();
-
-    let additional_types = variant_info.additional_types(RefType::No);
-    let mut impls = Vec::with_capacity(additional_types.len() + 1);
-    for explicit_type in iter::once(None).chain(additional_types.iter().map(Some)) {
-        let mut new_generics = input.generics.clone();
-
-        let mut initializers = Vec::with_capacity(infos.len());
-        let mut from_types = Vec::with_capacity(infos.len());
-        for (i, (info, field)) in infos.iter().zip(fields.iter()).enumerate() {
-            let field_type = &field.ty;
-            let variable = if fields.len() == 1 {
-                quote! { original }
-            } else {
-                let tuple_index = Index::from(i);
-                quote! { original.#tuple_index }
-            };
-            if let Some(type_) = explicit_type {
-                initializers.push(quote! {
-                    <#field_type as #trait_path<#type_>>::from(#variable)
-                });
-                from_types.push(quote! { #type_ });
-            } else if info.forward {
-                let type_param = format_ident!("__FromT{i}");
-                let sub_trait_path = quote! { #trait_path<#type_param> };
-                let type_where_clauses = quote! {
-                    where #field_type: #sub_trait_path
-                };
-                new_generics = add_where_clauses_for_new_ident(
-                    &new_generics,
-                    &[field],
-                    &type_param,
-                    type_where_clauses,
-                    true,
-                );
-                let casted_trait = quote! { <#field_type as #sub_trait_path> };
-                initializers.push(quote! { #casted_trait::from(#variable) });
-                from_types.push(quote! { #type_param });
-            } else {
-                initializers.push(variable);
-                from_types.push(quote! { #field_type });
+/// Representation of a [`From`] derive macro struct container attribute.
+///
+/// ```rust,ignore
+/// #[from(<types>)]
+/// #[from(forward)]
+/// ```
+enum StructAttribute {
+    /// [`Type`]s to derive [`From`].
+    Types(Punctuated<Type, token::Comma>),
+
+    /// Forward [`From`] implementation.
+    Forward,
+}
+
+impl StructAttribute {
+    /// Parses a [`StructAttribute`] from the provided [`syn::Attribute`]s.
+    fn parse_attrs(
+        attrs: impl AsRef<[syn::Attribute]>,
+        fields: &syn::Fields,
+    ) -> syn::Result<Option<Self>> {
+        Ok(attrs
+            .as_ref()
+            .iter()
+            .filter(|attr| attr.path().is_ident("from"))
+            .try_fold(None, |attrs, attr| {
+                let field_attr = attr.parse_args_with(|stream: ParseStream<'_>| {
+                    Self::parse(stream, fields)
+                })?;
+                match (attrs, field_attr) {
+                    (
+                        Some((path, StructAttribute::Types(mut tys))),
+                        StructAttribute::Types(more),
+                    ) => {
+                        tys.extend(more);
+                        Ok(Some((path, StructAttribute::Types(tys))))
+                    }
+                    (None, field_attr) => Ok(Some((attr.path(), field_attr))),
+                    _ => Err(syn::Error::new(
+                        attr.path().span(),
+                        "only single `#[from(...)]` attribute is allowed here",
+                    )),
+                }
+            })?
+            .map(|(_, attr)| attr))
+    }
+
+    /// Parses single [`StructAttribute`].
+    fn parse(input: ParseStream<'_>, fields: &syn::Fields) -> syn::Result<Self> {
+        let ahead = input.fork();
+        match ahead.parse::<syn::Path>() {
+            Ok(p) if p.is_ident("forward") => {
+                input.advance_to(&ahead);
+                Ok(Self::Forward)
             }
+            Ok(p) if p.is_ident("types") => legacy_error(&ahead, input.span(), fields),
+            _ => input
+                .parse_terminated(Type::parse, token::Comma)
+                .map(Self::Types),
         }
+    }
+}
+
+/// Representation of a [`From`] derive macro enum variant attribute.
+///
+/// ```rust,ignore
+/// #[from]
+/// #[from(<types>)]
+/// #[from(forward)]
+/// #[from(skip)]
+/// ```
+enum VariantAttribute {
+    /// Explicitly derive [`From`].
+    From,
+
+    /// [`Type`]s to derive [`From`].
+    Types(Punctuated<Type, token::Comma>),
 
-        let body = multi_field_data.initializer(&initializers);
-        let (impl_generics, _, where_clause) = new_generics.split_for_impl();
-        let (_, ty_generics, _) = input.generics.split_for_impl();
+    /// Forward [`From`] implementation.
+    Forward,
 
-        impls.push(quote! {
-            #[automatically_derived]
-            impl #impl_generics #trait_path<(#(#from_types),*)> for
-                #input_type #ty_generics #where_clause {
+    /// Skip variant.
+    Skip,
+}
+
+impl VariantAttribute {
+    /// Parses a [`VariantAttribute`] from the provided [`syn::Attribute`]s.
+    fn parse_attrs(
+        attrs: impl AsRef<[syn::Attribute]>,
+        fields: &syn::Fields,
+    ) -> syn::Result<Option<Self>> {
+        Ok(attrs
+            .as_ref()
+            .iter()
+            .filter(|attr| attr.path().is_ident("from"))
+            .try_fold(None, |mut attrs, attr| {
+                let field_attr = Self::parse_attr(attr, fields)?;
+                if let Some((path, _)) = attrs.replace((attr.path(), field_attr)) {
+                    Err(syn::Error::new(
+                        path.span(),
+                        "only single `#[from(...)]` attribute is allowed here",
+                    ))
+                } else {
+                    Ok(attrs)
+                }
+            })?
+            .map(|(_, attr)| attr))
+    }
+
+    /// Parses a [`VariantAttribute`] from the single provided [`syn::Attribute`].
+    fn parse_attr(attr: &syn::Attribute, fields: &syn::Fields) -> syn::Result<Self> {
+        if matches!(attr.meta, syn::Meta::Path(_)) {
+            return Ok(Self::From);
+        }
 
-                #[inline]
-                fn from(original: (#(#from_types),*)) -> #input_type #ty_generics {
-                    #body
+        attr.parse_args_with(|input: ParseStream<'_>| {
+            let ahead = input.fork();
+            match ahead.parse::<syn::Path>() {
+                Ok(p) if p.is_ident("forward") => {
+                    input.advance_to(&ahead);
+                    Ok(Self::Forward)
+                }
+                Ok(p) if p.is_ident("skip") || p.is_ident("ignore") => {
+                    input.advance_to(&ahead);
+                    Ok(Self::Skip)
+                }
+                Ok(p) if p.is_ident("types") => {
+                    legacy_error(&ahead, input.span(), fields)
                 }
+                _ => input
+                    .parse_terminated(Type::parse, token::Comma)
+                    .map(Self::Types),
             }
-        });
+        })
     }
+}
 
-    quote! { #( #impls )* }
+impl From<StructAttribute> for VariantAttribute {
+    fn from(value: StructAttribute) -> Self {
+        match value {
+            StructAttribute::Types(tys) => Self::Types(tys),
+            StructAttribute::Forward => Self::Forward,
+        }
+    }
 }
 
-fn enum_from(input: &DeriveInput, state: State) -> TokenStream {
-    let mut tokens = TokenStream::new();
-
-    let mut variants_per_types = HashMap::default();
-    for variant_state in state.enabled_variant_data().variant_states {
-        let multi_field_data = variant_state.enabled_fields_data();
-        let MultiFieldData { field_types, .. } = multi_field_data.clone();
-        variants_per_types
-            .entry(field_types.clone())
-            .or_insert_with(Vec::new)
-            .push(variant_state);
+/// Expansion of a macro for generating [`From`] implementation of a struct or
+/// enum.
+struct Expansion<'a> {
+    /// [`From`] attributes.
+    ///
+    /// As a [`VariantAttribute`] is superset of a [`StructAttribute`], we use
+    /// it for both derives.
+    attrs: Option<&'a VariantAttribute>,
+
+    /// Struct or enum [`Ident`].
+    ident: &'a Ident,
+
+    /// Variant [`Ident`] in case of enum expansion.
+    variant: Option<&'a Ident>,
+
+    /// Struct or variant [`syn::Fields`].
+    fields: &'a syn::Fields,
+
+    /// Struct or enum [`syn::Generics`].
+    generics: &'a syn::Generics,
+
+    /// Indicator whether one of the enum variants has
+    /// [`VariantAttribute::From`], [`VariantAttribute::Types`] or
+    /// [`VariantAttribute::Forward`].
+    ///
+    /// Always [`false`] for structs.
+    has_explicit_from: bool,
+}
+
+impl<'a> Expansion<'a> {
+    /// Expands [`From`] implementations for a struct or an enum variant.
+    fn expand(&self) -> syn::Result<TokenStream> {
+        let ident = self.ident;
+        let field_tys = self.fields.iter().map(|f| &f.ty).collect::<Vec<_>>();
+        let (impl_gens, ty_gens, where_clause) = self.generics.split_for_impl();
+
+        let skip_variant = self.has_explicit_from
+            || (self.variant.is_some() && self.fields.is_empty());
+        match (self.attrs, skip_variant) {
+            (Some(VariantAttribute::Types(tys)), _) => {
+                tys.iter().map(|ty| {
+                    let variant = self.variant.iter();
+
+                    let mut from_tys = self.validate_type(ty)?;
+                    let init = self.expand_fields(|ident, ty, index| {
+                        let ident = ident.into_iter();
+                        let index = index.into_iter();
+                        let from_ty = from_tys.next().unwrap_or_else(|| unreachable!());
+                        quote! {
+                            #( #ident: )* <#ty as ::core::convert::From<#from_ty>>::from(
+                                value #( .#index )*
+                            ),
+                        }
+                    });
+
+                    Ok(quote! {
+                        #[automatically_derived]
+                        impl #impl_gens ::core::convert::From<#ty>
+                         for #ident #ty_gens #where_clause
+                        {
+                            #[inline]
+                            fn from(value: #ty) -> Self {
+                                #ident #( :: #variant )* #init
+                            }
+                        }
+                    })
+                })
+                .collect()
+            }
+            (Some(VariantAttribute::From), _) | (None, false) => {
+                let variant = self.variant.iter();
+                let init = self.expand_fields(|ident, _, index| {
+                    let ident = ident.into_iter();
+                    let index = index.into_iter();
+                    quote! { #( #ident: )* value #( . #index )*, }
+                });
+
+                Ok(quote! {
+                    #[automatically_derived]
+                    impl #impl_gens ::core::convert::From<(#( #field_tys ),*)>
+                     for #ident #ty_gens #where_clause
+                    {
+                        #[inline]
+                        fn from(value: (#( #field_tys ),*)) -> Self {
+                            #ident #( :: #variant )* #init
+                        }
+                    }
+                })
+            }
+            (Some(VariantAttribute::Forward), _) => {
+                let mut i = 0;
+                let mut gen_idents = Vec::with_capacity(self.fields.len());
+                let init = self.expand_fields(|ident, ty, index| {
+                    let ident = ident.into_iter();
+                    let index = index.into_iter();
+                    let gen_ident = format_ident!("__FromT{i}");
+                    let out = quote! {
+                        #( #ident: )* <#ty as ::core::convert::From<#gen_ident>>::from(
+                            value #( .#index )*
+                        ),
+                    };
+                    gen_idents.push(gen_ident);
+                    i += 1;
+                    out
+                });
+
+                let variant = self.variant.iter();
+                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: ::core::convert::From<#ident> },
+                        );
+                        generics
+                            .params
+                            .push(syn::TypeParam::from(ident.clone()).into());
+                    }
+                    generics
+                };
+                let (impl_gens, _, where_clause) = generics.split_for_impl();
+
+                Ok(quote! {
+                    #[automatically_derived]
+                    impl #impl_gens ::core::convert::From<(#( #gen_idents ),*)>
+                     for #ident #ty_gens #where_clause
+                    {
+                        #[inline]
+                        fn from(value: (#( #gen_idents ),*)) -> Self {
+                            #ident #(:: #variant)* #init
+                        }
+                    }
+                })
+            }
+            (Some(VariantAttribute::Skip), _) | (None, true) => {
+                Ok(TokenStream::new())
+            }
+        }
     }
-    for (ref field_types, ref variant_states) in variants_per_types {
-        for variant_state in variant_states {
-            // Don't derive From for variants without any fields
-            if field_types.is_empty() {
-                continue;
+
+    /// Expands fields initialization wrapped into [`token::Brace`]s in case of
+    /// [`syn::FieldsNamed`], or [`token::Paren`] in case of
+    /// [`syn::FieldsUnnamed`].
+    fn expand_fields(
+        &self,
+        mut wrap: impl FnMut(Option<&Ident>, &syn::Type, Option<syn::Index>) -> TokenStream,
+    ) -> TokenStream {
+        let surround = match self.fields {
+            syn::Fields::Named(_) | syn::Fields::Unnamed(_) => {
+                Some(|tokens| match self.fields {
+                    syn::Fields::Named(named) => {
+                        let mut out = TokenStream::new();
+                        named
+                            .brace_token
+                            .surround(&mut out, |out| out.append_all(tokens));
+                        out
+                    }
+                    syn::Fields::Unnamed(unnamed) => {
+                        let mut out = TokenStream::new();
+                        unnamed
+                            .paren_token
+                            .surround(&mut out, |out| out.append_all(tokens));
+                        out
+                    }
+                    syn::Fields::Unit => unreachable!(),
+                })
+            }
+            syn::Fields::Unit => None,
+        };
+
+        surround
+            .map(|surround| {
+                surround(if self.fields.len() == 1 {
+                    let field = self
+                        .fields
+                        .iter()
+                        .next()
+                        .unwrap_or_else(|| unreachable!("self.fields.len() == 1"));
+                    wrap(field.ident.as_ref(), &field.ty, None)
+                } else {
+                    self.fields
+                        .iter()
+                        .enumerate()
+                        .map(|(i, field)| {
+                            wrap(field.ident.as_ref(), &field.ty, Some(i.into()))
+                        })
+                        .collect()
+                })
+            })
+            .unwrap_or_default()
+    }
+
+    /// Validates the provided [`Type`] against [`syn::Fields`].
+    fn validate_type<'t>(
+        &self,
+        ty: &'t Type,
+    ) -> syn::Result<impl Iterator<Item = &'t TokenStream>> {
+        match ty {
+            Type::Tuple { items, .. } if self.fields.len() > 1 => {
+                match self.fields.len().cmp(&items.len()) {
+                    cmp::Ordering::Greater => {
+                        return Err(syn::Error::new(
+                            ty.span(),
+                            format!(
+                                "wrong tuple length: expected {}, found {}. \
+                                 Consider adding {} more type{}: `({})`",
+                                self.fields.len(),
+                                items.len(),
+                                self.fields.len() - items.len(),
+                                if self.fields.len() - items.len() > 1 {
+                                    "s"
+                                } else {
+                                    ""
+                                },
+                                items
+                                    .iter()
+                                    .map(|item| item.to_string())
+                                    .chain(
+                                        (0..(self.fields.len() - items.len()))
+                                            .map(|_| "_".to_string())
+                                    )
+                                    .collect::<Vec<_>>()
+                                    .join(", "),
+                            ),
+                        ));
+                    }
+                    cmp::Ordering::Less => {
+                        return Err(syn::Error::new(
+                            ty.span(),
+                            format!(
+                                "wrong tuple length: expected {}, found {}. \
+                                 Consider removing last {} type{}: `({})`",
+                                self.fields.len(),
+                                items.len(),
+                                items.len() - self.fields.len(),
+                                if items.len() - self.fields.len() > 1 {
+                                    "s"
+                                } else {
+                                    ""
+                                },
+                                items
+                                    .iter()
+                                    .take(self.fields.len())
+                                    .map(|item| item.to_string())
+                                    .collect::<Vec<_>>()
+                                    .join(", "),
+                            ),
+                        ));
+                    }
+                    cmp::Ordering::Equal => {}
+                }
             }
-            struct_from(input, variant_state).to_tokens(&mut tokens);
+            Type::Other(other) if self.fields.len() > 1 => {
+                if self.fields.len() > 1 {
+                    return Err(syn::Error::new(
+                        other.span(),
+                        format!(
+                            "expected tuple: `({}, {})`",
+                            other,
+                            (0..(self.fields.len() - 1))
+                                .map(|_| "_")
+                                .collect::<Vec<_>>()
+                                .join(", "),
+                        ),
+                    ));
+                }
+            }
+            Type::Tuple { .. } | Type::Other(_) => {}
+        }
+        Ok(match ty {
+            Type::Tuple { items, .. } => Either::Left(items.iter()),
+            Type::Other(other) => Either::Right(iter::once(other)),
+        })
+    }
+}
+
+/// Either [`Left`] or [`Right`].
+///
+/// [`Left`]: Either::Left
+/// [`Right`]: Either::Right
+enum Either<L, R> {
+    /// Left variant.
+    Left(L),
+
+    /// Right variant.
+    Right(R),
+}
+
+impl<L, R, T> Iterator for Either<L, R>
+where
+    L: Iterator<Item = T>,
+    R: Iterator<Item = T>,
+{
+    type Item = T;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        match self {
+            Either::Left(left) => left.next(),
+            Either::Right(right) => right.next(),
         }
     }
-    tokens
+}
+
+/// Constructs a [`syn::Error`] for legacy syntax: `#[from(types(i32, "&str"))]`.
+fn legacy_error<T>(
+    tokens: ParseStream<'_>,
+    span: Span,
+    fields: &syn::Fields,
+) -> syn::Result<T> {
+    let content;
+    syn::parenthesized!(content in tokens);
+
+    let types = content
+        .parse_terminated(polyfill::NestedMeta::parse, token::Comma)?
+        .into_iter()
+        .map(|meta| {
+            let value = match meta {
+                polyfill::NestedMeta::Meta(meta) => {
+                    meta.into_token_stream().to_string()
+                }
+                polyfill::NestedMeta::Lit(syn::Lit::Str(str)) => str.value(),
+                polyfill::NestedMeta::Lit(_) => unreachable!(),
+            };
+            if fields.len() > 1 {
+                format!(
+                    "({})",
+                    fields
+                        .iter()
+                        .map(|_| value.clone())
+                        .collect::<Vec<_>>()
+                        .join(", "),
+                )
+            } else {
+                value
+            }
+        })
+        .chain(match fields.len() {
+            0 => Either::Left(iter::empty()),
+            1 => Either::Right(iter::once(
+                fields
+                    .iter()
+                    .next()
+                    .unwrap_or_else(|| unreachable!("fields.len() == 1"))
+                    .ty
+                    .to_token_stream()
+                    .to_string(),
+            )),
+            _ => Either::Right(iter::once(format!(
+                "({})",
+                fields
+                    .iter()
+                    .map(|f| f.ty.to_token_stream().to_string())
+                    .collect::<Vec<_>>()
+                    .join(", ")
+            ))),
+        })
+        .collect::<Vec<_>>()
+        .join(", ");
+
+    Err(syn::Error::new(
+        span,
+        format!("legacy syntax, remove `types` and use `{types}` instead"),
+    ))
 }
diff --git a/impl/src/lib.rs b/impl/src/lib.rs
index 1760086..751fba5 100644
--- a/impl/src/lib.rs
+++ b/impl/src/lib.rs
@@ -57,6 +57,8 @@ mod mul_helpers;
 mod mul_like;
 #[cfg(feature = "not")]
 mod not_like;
+#[cfg(any(feature = "debug", feature = "display", feature = "from"))]
+pub(crate) mod parsing;
 #[cfg(feature = "sum")]
 mod sum_like;
 #[cfg(feature = "try_into")]
diff --git a/impl/src/parsing.rs b/impl/src/parsing.rs
new file mode 100644
index 0000000..af571ed
--- /dev/null
+++ b/impl/src/parsing.rs
@@ -0,0 +1,459 @@
+//! Common parsing utilities for derive macros.
+//!
+//! Fair parsing of [`syn::Type`] and [`syn::Expr`] requires [`syn`]'s `full`
+//! feature to be enabled, which unnecessary increases compile times. As we
+//! don't have complex AST manipulation, usually requiring only understanding
+//! where syntax item begins and ends, simpler manual parsing is implemented.
+
+use proc_macro2::{Delimiter, Spacing, TokenStream};
+use quote::ToTokens;
+use syn::{
+    buffer::Cursor,
+    parse::{Parse, ParseStream},
+    punctuated::Punctuated,
+    spanned::Spanned as _,
+    token, Error, Ident, Result,
+};
+
+/// [`syn::Type`] [`Parse`]ing polyfill.
+#[derive(Debug)]
+pub(crate) enum Type {
+    /// [`syn::Type::Tuple`] [`Parse`]ing polyfill.
+    Tuple {
+        paren: token::Paren,
+        items: Punctuated<TokenStream, token::Comma>,
+    },
+
+    /// Every other [`syn::Type`] variant.
+    Other(TokenStream),
+}
+
+impl Parse for Type {
+    fn parse(input: ParseStream) -> Result<Self> {
+        input.step(|c| {
+            let outer = *c;
+
+            if let Some((mut cursor, paren_span, next_item)) =
+                outer.group(Delimiter::Parenthesis)
+            {
+                let mut items = Punctuated::new();
+                while !cursor.eof() {
+                    let (stream, c) = Self::parse_other(cursor).ok_or_else(|| {
+                        Error::new(cursor.span(), "failed to parse type")
+                    })?;
+                    items.push_value(stream);
+                    cursor = c;
+                    if let Some((p, c)) = punct(',')(cursor) {
+                        items.push_punct(token::Comma(p.span()));
+                        cursor = c;
+                    }
+                }
+                // `(Type)` is equivalent to `Type`, so isn't top-level tuple.
+                if items.len() == 1 && !items.trailing_punct() {
+                    let stream = outer
+                        .token_tree()
+                        .unwrap_or_else(|| unreachable!())
+                        .0
+                        .into_token_stream();
+                    Ok((Type::Other(stream), next_item))
+                } else {
+                    Ok((
+                        Type::Tuple {
+                            paren: token::Paren(paren_span),
+                            items,
+                        },
+                        next_item,
+                    ))
+                }
+            } else {
+                Self::parse_other(outer)
+                    .map(|(s, c)| (Self::Other(s), c))
+                    .ok_or_else(|| Error::new(outer.span(), "failed to parse type"))
+            }
+        })
+    }
+}
+
+impl ToTokens for Type {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        match self {
+            Type::Tuple { paren, items } => {
+                paren.surround(tokens, |tokens| items.to_tokens(tokens))
+            }
+            Type::Other(other) => other.to_tokens(tokens),
+        }
+    }
+}
+
+impl Type {
+    /// Parses a single [`Type::Other`].
+    pub fn parse_other(c: Cursor<'_>) -> Option<(TokenStream, Cursor<'_>)> {
+        take_until1(
+            alt([&mut balanced_pair(punct('<'), punct('>')), &mut token_tree]),
+            punct(','),
+        )(c)
+    }
+}
+
+/// [`syn::Expr`] [`Parse`]ing polyfill.
+#[derive(Debug)]
+pub(crate) enum Expr {
+    /// [`syn::Expr::Path`] of length 1 [`Parse`]ing polyfill.
+    Ident(Ident),
+
+    /// Every other [`syn::Expr`] variant.
+    Other(TokenStream),
+}
+
+impl Expr {
+    /// Returns an [`Ident`] in case this [`Expr`] is represented only by it.
+    pub(crate) fn ident(&self) -> Option<&Ident> {
+        match self {
+            Self::Ident(ident) => Some(ident),
+            Self::Other(_) => None,
+        }
+    }
+}
+
+impl Parse for Expr {
+    fn parse(input: ParseStream) -> Result<Self> {
+        if let Ok(ident) = input.step(|c| {
+            c.ident()
+                .filter(|(_, c)| c.eof() || punct(',')(*c).is_some())
+                .ok_or_else(|| Error::new(c.span(), "expected `ident(,|eof)`"))
+        }) {
+            Ok(Self::Ident(ident))
+        } else {
+            input.step(|c| {
+                take_until1(
+                    alt([
+                        &mut seq([
+                            &mut colon2,
+                            &mut balanced_pair(punct('<'), punct('>')),
+                        ]),
+                        &mut seq([
+                            &mut balanced_pair(punct('<'), punct('>')),
+                            &mut colon2,
+                        ]),
+                        &mut balanced_pair(punct('|'), punct('|')),
+                        &mut token_tree,
+                    ]),
+                    punct(','),
+                )(*c)
+                .map(|(stream, cursor)| (Self::Other(stream), cursor))
+                .ok_or_else(|| Error::new(c.span(), "failed to parse expression"))
+            })
+        }
+    }
+}
+
+impl ToTokens for Expr {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        match self {
+            Self::Ident(ident) => ident.to_tokens(tokens),
+            Self::Other(other) => other.to_tokens(tokens),
+        }
+    }
+}
+
+/// Result of parsing.
+type ParsingResult<'a> = Option<(TokenStream, Cursor<'a>)>;
+
+/// Tries to parse a [`syn::token::Colon2`].
+pub fn colon2(c: Cursor<'_>) -> ParsingResult<'_> {
+    seq([
+        &mut punct_with_spacing(':', Spacing::Joint),
+        &mut punct(':'),
+    ])(c)
+}
+
+/// Tries to parse a [`punct`] with [`Spacing`].
+pub fn punct_with_spacing(
+    p: char,
+    spacing: Spacing,
+) -> impl FnMut(Cursor<'_>) -> ParsingResult<'_> {
+    move |c| {
+        c.punct().and_then(|(punct, c)| {
+            (punct.as_char() == p && punct.spacing() == spacing)
+                .then(|| (punct.into_token_stream(), c))
+        })
+    }
+}
+
+/// Tries to parse a [`Punct`].
+///
+/// [`Punct`]: proc_macro2::Punct
+pub fn punct(p: char) -> impl FnMut(Cursor<'_>) -> ParsingResult<'_> {
+    move |c| {
+        c.punct().and_then(|(punct, c)| {
+            (punct.as_char() == p).then(|| (punct.into_token_stream(), c))
+        })
+    }
+}
+
+/// Tries to parse any [`TokenTree`].
+///
+/// [`TokenTree`]: proc_macro2::TokenTree
+pub fn token_tree(c: Cursor<'_>) -> ParsingResult<'_> {
+    c.token_tree().map(|(tt, c)| (tt.into_token_stream(), c))
+}
+
+/// Parses until balanced amount of `open` and `close` or eof.
+///
+/// [`Cursor`] should be pointing **right after** the first `open`ing.
+pub fn balanced_pair(
+    mut open: impl FnMut(Cursor<'_>) -> ParsingResult<'_>,
+    mut close: impl FnMut(Cursor<'_>) -> ParsingResult<'_>,
+) -> impl FnMut(Cursor<'_>) -> ParsingResult<'_> {
+    move |c| {
+        let (mut out, mut c) = open(c)?;
+        let mut count = 1;
+
+        while count != 0 {
+            let (stream, cursor) = if let Some(closing) = close(c) {
+                count -= 1;
+                closing
+            } else if let Some(opening) = open(c) {
+                count += 1;
+                opening
+            } else {
+                let (tt, c) = c.token_tree()?;
+                (tt.into_token_stream(), c)
+            };
+            out.extend(stream);
+            c = cursor;
+        }
+
+        Some((out, c))
+    }
+}
+
+/// Tries to execute the provided sequence of `parsers`.
+pub fn seq<const N: usize>(
+    mut parsers: [&mut dyn FnMut(Cursor<'_>) -> ParsingResult<'_>; N],
+) -> impl FnMut(Cursor<'_>) -> ParsingResult<'_> + '_ {
+    move |c| {
+        parsers
+            .iter_mut()
+            .fold(Some((TokenStream::new(), c)), |out, parser| {
+                let (mut out, mut c) = out?;
+                let (stream, cursor) = parser(c)?;
+                out.extend(stream);
+                c = cursor;
+                Some((out, c))
+            })
+    }
+}
+
+/// Tries to execute the first successful parser.
+pub fn alt<const N: usize>(
+    mut parsers: [&mut dyn FnMut(Cursor<'_>) -> ParsingResult<'_>; N],
+) -> impl FnMut(Cursor<'_>) -> ParsingResult<'_> + '_ {
+    move |c| {
+        parsers
+            .iter_mut()
+            .find_map(|parser| parser(c).map(|(s, c)| (s, c)))
+    }
+}
+
+/// Parses with `basic` while `until` fails. Returns [`None`] in case
+/// `until` succeeded initially or `basic` never succeeded. Doesn't consume
+/// tokens parsed by `until`.
+pub fn take_until1<P, U>(
+    mut parser: P,
+    mut until: U,
+) -> impl FnMut(Cursor<'_>) -> ParsingResult<'_>
+where
+    P: FnMut(Cursor<'_>) -> ParsingResult<'_>,
+    U: FnMut(Cursor<'_>) -> ParsingResult<'_>,
+{
+    move |mut cursor| {
+        let mut out = TokenStream::new();
+        let mut parsed = false;
+
+        loop {
+            if cursor.eof() || until(cursor).is_some() {
+                return parsed.then_some((out, cursor));
+            }
+
+            let (stream, c) = parser(cursor)?;
+            out.extend(stream);
+            cursor = c;
+            parsed = true;
+        }
+    }
+}
+
+#[cfg(test)]
+mod spec {
+    use std::{fmt::Debug, str::FromStr};
+
+    use itertools::Itertools as _;
+    use proc_macro2::TokenStream;
+    use quote::ToTokens;
+    use syn::{
+        parse::{Parse, Parser as _},
+        punctuated::Punctuated,
+        token::Comma,
+    };
+
+    use super::{Expr, Type};
+
+    fn assert<'a, T: Debug + Parse + ToTokens>(
+        input: &'a str,
+        parsed: impl AsRef<[&'a str]>,
+    ) {
+        let parsed = parsed.as_ref();
+        let punctuated = Punctuated::<T, Comma>::parse_terminated
+            .parse2(TokenStream::from_str(input).unwrap())
+            .unwrap();
+
+        assert_eq!(
+            parsed.len(),
+            punctuated.len(),
+            "Wrong length\n\
+             Expected: {parsed:?}\n\
+             Found: {punctuated:?}",
+        );
+
+        punctuated
+            .iter()
+            .map(|ty| ty.to_token_stream().to_string())
+            .zip(parsed)
+            .enumerate()
+            .for_each(|(i, (found, expected))| {
+                assert_eq!(
+                    *expected, &found,
+                    "Mismatch at index {i}\n\
+                     Expected: {parsed:?}\n\
+                     Found: {punctuated:?}",
+                );
+            });
+    }
+
+    mod tuple {
+        use super::*;
+
+        #[test]
+        fn zst_is_tuple() {
+            let zst = "()";
+            match syn::parse_str::<Type>(zst).unwrap() {
+                Type::Tuple { items, .. } => {
+                    assert!(items.is_empty(), "Expected empty tuple, found: {items:?}");
+                }
+                other => panic!("Expected `Type::Tuple {{ .. }}`, found: {other:?}"),
+            }
+        }
+
+        #[test]
+        fn group_not_tuple() {
+            let group = "(Type)";
+            match syn::parse_str::<Type>(group).unwrap() {
+                Type::Other(tokens) => {
+                    assert_eq!(tokens.to_string(), group);
+                }
+                tuple => panic!("Expected `Type::Other(_)`, found: {tuple:?}"),
+            }
+        }
+
+        #[test]
+        fn single_element_tuple() {
+            let tuple = "(Type,)";
+            match syn::parse_str::<Type>(tuple).unwrap() {
+                Type::Tuple { items, .. } => {
+                    assert_eq!(
+                        items.len(),
+                        1,
+                        "Expected empty tuple, found: {items:?}",
+                    );
+                    assert_eq!(items.first().unwrap().to_string(), "Type");
+                }
+                other => panic!("Expected `Type::Tuple {{ .. }}`, found: {other:?}"),
+            }
+        }
+
+        #[test]
+        fn cases() {
+            let cases = [
+                "[Type ; 3]",
+                "fn (usize) -> bool",
+                "for <'a > fn (&'a usize) -> bool",
+                "(Type)",
+                "path :: to :: Type",
+                "path :: to :: Generic < Type >",
+                "< Type as Trait >:: Assoc",
+                "< Type as Trait >:: Assoc < GAT >",
+                "* const ()",
+                "* mut ()",
+                "& i32",
+                "&'static str",
+                "& [str]",
+                "dyn Trait",
+                "dyn Trait + Send",
+                "()",
+                "(Type ,)",
+                "(Type , Type)",
+                "(Type , Type ,)",
+            ];
+
+            assert::<Type>("", []);
+            for i in 1..4 {
+                for permutations in cases.into_iter().permutations(i) {
+                    let mut input = permutations.join(",");
+                    assert::<Type>(&input, &permutations);
+                    input.push(',');
+                    assert::<Type>(&input, &permutations);
+                }
+            }
+        }
+    }
+
+    mod expr {
+        use super::*;
+
+        #[test]
+        fn cases() {
+            let cases = [
+                "ident",
+                "[a , b , c , d]",
+                "counter += 1",
+                "async { fut . await }",
+                "a < b",
+                "a > b",
+                "{ let x = (a , b) ; }",
+                "invoke (a , b)",
+                "foo as f64",
+                "| a , b | a + b",
+                "obj . k",
+                "for pat in expr { break pat ; }",
+                "if expr { true } else { false }",
+                "vector [2]",
+                "1",
+                "\"foo\"",
+                "loop { break i ; }",
+                "format ! (\"{}\" , q)",
+                "match n { Some (n) => { } , None => { } }",
+                "x . foo ::< T > (a , b)",
+                "x . foo ::< T < [T < T >; if a < b { 1 } else { 2 }] >, { a < b } > (a , b)",
+                "(a + b)",
+                "i32 :: MAX",
+                "1 .. 2",
+                "& a",
+                "[0u8 ; N]",
+                "(a , b , c , d)",
+                "< Ty as Trait > :: T",
+                "< Ty < Ty < T >, { a < b } > as Trait < T > > :: T",
+            ];
+
+            assert::<Expr>("", []);
+            for i in 1..4 {
+                for permutations in cases.into_iter().permutations(i) {
+                    let mut input = permutations.clone().join(",");
+                    assert::<Expr>(&input, &permutations);
+                    input.push(',');
+                    assert::<Expr>(&input, &permutations);
+                }
+            }
+        }
+    }
+}
diff --git a/impl/src/utils.rs b/impl/src/utils.rs
index 3dd24ce..f192205 100644
--- a/impl/src/utils.rs
+++ b/impl/src/utils.rs
@@ -1111,7 +1111,7 @@ fn parse_punctuated_nested_meta(
 // TODO: Remove this eventually, once all macros migrate to
 //       custom typed attributes parsing.
 /// Polyfill for [`syn`] 1.x AST.
-mod polyfill {
+pub(crate) mod polyfill {
     use proc_macro2::TokenStream;
     use quote::ToTokens;
     use syn::{
@@ -1121,7 +1121,7 @@ mod polyfill {
     };
 
     #[derive(Clone)]
-    pub(super) enum PathOrKeyword {
+    pub(crate) enum PathOrKeyword {
         Path(syn::Path),
         Keyword(syn::Ident),
     }
@@ -1173,7 +1173,7 @@ mod polyfill {
     }
 
     #[derive(Clone)]
-    pub(super) struct MetaList {
+    pub(crate) struct MetaList {
         pub(super) path: PathOrKeyword,
         pub(super) tokens: TokenStream,
     }
@@ -1205,7 +1205,7 @@ mod polyfill {
     }
 
     #[derive(Clone)]
-    pub(super) enum Meta {
+    pub(crate) enum Meta {
         Path(PathOrKeyword),
         List(MetaList),
     }
@@ -1236,7 +1236,7 @@ mod polyfill {
     }
 
     #[derive(Clone)]
-    pub(super) enum NestedMeta {
+    pub(crate) enum NestedMeta {
         Meta(Meta),
         Lit(syn::Lit),
     }


Test patch:
diff --git a/tests/compile_fail/from/legacy_enum_attribute.rs b/tests/compile_fail/from/legacy_enum_attribute.rs
new file mode 100644
index 0000000..26551a7
--- /dev/null
+++ b/tests/compile_fail/from/legacy_enum_attribute.rs
@@ -0,0 +1,7 @@
+#[derive(derive_more::From)]
+enum Foo {
+    #[from(types(i32, "&str"))]
+    Bar(String),
+}
+
+fn main() {}
diff --git a/tests/compile_fail/from/legacy_enum_attribute.stderr b/tests/compile_fail/from/legacy_enum_attribute.stderr
new file mode 100644
index 0000000..7d2d923
--- /dev/null
+++ b/tests/compile_fail/from/legacy_enum_attribute.stderr
@@ -0,0 +1,5 @@
+error: legacy syntax, remove `types` and use `i32, &str, String` instead
+ --> tests/compile_fail/from/legacy_enum_attribute.rs:3:12
+  |
+3 |     #[from(types(i32, "&str"))]
+  |            ^^^^^
diff --git a/tests/compile_fail/from/legacy_struct_attribute.rs b/tests/compile_fail/from/legacy_struct_attribute.rs
new file mode 100644
index 0000000..1c7d9c5
--- /dev/null
+++ b/tests/compile_fail/from/legacy_struct_attribute.rs
@@ -0,0 +1,8 @@
+#[derive(derive_more::From)]
+#[from(types(i32, "&str"))]
+struct Foo {
+    foo: String,
+    bar: String,
+}
+
+fn main() {}
diff --git a/tests/compile_fail/from/legacy_struct_attribute.stderr b/tests/compile_fail/from/legacy_struct_attribute.stderr
new file mode 100644
index 0000000..6e5300a
--- /dev/null
+++ b/tests/compile_fail/from/legacy_struct_attribute.stderr
@@ -0,0 +1,5 @@
+error: legacy syntax, remove `types` and use `(i32, i32), (&str, &str), (String, String)` instead
+ --> tests/compile_fail/from/legacy_struct_attribute.rs:2:8
+  |
+2 | #[from(types(i32, "&str"))]
+  |        ^^^^^
diff --git a/tests/compile_fail/from/multiple_enum_attributes.rs b/tests/compile_fail/from/multiple_enum_attributes.rs
new file mode 100644
index 0000000..6fc73ee
--- /dev/null
+++ b/tests/compile_fail/from/multiple_enum_attributes.rs
@@ -0,0 +1,8 @@
+#[derive(derive_more::From)]
+enum Foo {
+    #[from(i32)]
+    #[from(forward)]
+    Bar(i32),
+}
+
+fn main() {}
diff --git a/tests/compile_fail/from/multiple_enum_attributes.stderr b/tests/compile_fail/from/multiple_enum_attributes.stderr
new file mode 100644
index 0000000..966e989
--- /dev/null
+++ b/tests/compile_fail/from/multiple_enum_attributes.stderr
@@ -0,0 +1,5 @@
+error: only single `#[from(...)]` attribute is allowed here
+ --> tests/compile_fail/from/multiple_enum_attributes.rs:3:7
+  |
+3 |     #[from(i32)]
+  |       ^^^^
diff --git a/tests/compile_fail/from/multiple_struct_attributes.rs b/tests/compile_fail/from/multiple_struct_attributes.rs
new file mode 100644
index 0000000..4fa2a84
--- /dev/null
+++ b/tests/compile_fail/from/multiple_struct_attributes.rs
@@ -0,0 +1,6 @@
+#[derive(derive_more::From)]
+#[from(i32)]
+#[from(forward)]
+struct Foo(i32);
+
+fn main() {}
diff --git a/tests/compile_fail/from/multiple_struct_attributes.stderr b/tests/compile_fail/from/multiple_struct_attributes.stderr
new file mode 100644
index 0000000..6c3c8ca
--- /dev/null
+++ b/tests/compile_fail/from/multiple_struct_attributes.stderr
@@ -0,0 +1,5 @@
+error: only single `#[from(...)]` attribute is allowed here
+ --> tests/compile_fail/from/multiple_struct_attributes.rs:3:3
+  |
+3 | #[from(forward)]
+  |   ^^^^
diff --git a/tests/compile_fail/from/struct_tuple_no_parens.rs b/tests/compile_fail/from/struct_tuple_no_parens.rs
new file mode 100644
index 0000000..8dd4462
--- /dev/null
+++ b/tests/compile_fail/from/struct_tuple_no_parens.rs
@@ -0,0 +1,8 @@
+#[derive(derive_more::From)]
+#[from(i16, i16)]
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+fn main() {}
diff --git a/tests/compile_fail/from/struct_tuple_no_parens.stderr b/tests/compile_fail/from/struct_tuple_no_parens.stderr
new file mode 100644
index 0000000..18b1806
--- /dev/null
+++ b/tests/compile_fail/from/struct_tuple_no_parens.stderr
@@ -0,0 +1,5 @@
+error: expected tuple: `(i16, _)`
+ --> tests/compile_fail/from/struct_tuple_no_parens.rs:2:8
+  |
+2 | #[from(i16, i16)]
+  |        ^^^
diff --git a/tests/compile_fail/from/struct_tuple_too_long.rs b/tests/compile_fail/from/struct_tuple_too_long.rs
new file mode 100644
index 0000000..ed56c0a
--- /dev/null
+++ b/tests/compile_fail/from/struct_tuple_too_long.rs
@@ -0,0 +1,8 @@
+#[derive(derive_more::From)]
+#[from((i16, i16, i16))]
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+fn main() {}
diff --git a/tests/compile_fail/from/struct_tuple_too_long.stderr b/tests/compile_fail/from/struct_tuple_too_long.stderr
new file mode 100644
index 0000000..3ee791b
--- /dev/null
+++ b/tests/compile_fail/from/struct_tuple_too_long.stderr
@@ -0,0 +1,5 @@
+error: wrong tuple length: expected 2, found 3. Consider removing last 1 type: `(i16, i16)`
+ --> tests/compile_fail/from/struct_tuple_too_long.rs:2:8
+  |
+2 | #[from((i16, i16, i16))]
+  |        ^^^^^^^^^^^^^^^
diff --git a/tests/compile_fail/from/struct_tuple_too_short.rs b/tests/compile_fail/from/struct_tuple_too_short.rs
new file mode 100644
index 0000000..2f51473
--- /dev/null
+++ b/tests/compile_fail/from/struct_tuple_too_short.rs
@@ -0,0 +1,8 @@
+#[derive(derive_more::From)]
+#[from((i16,))]
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+fn main() {}
diff --git a/tests/compile_fail/from/struct_tuple_too_short.stderr b/tests/compile_fail/from/struct_tuple_too_short.stderr
new file mode 100644
index 0000000..121c5bb
--- /dev/null
+++ b/tests/compile_fail/from/struct_tuple_too_short.stderr
@@ -0,0 +1,5 @@
+error: wrong tuple length: expected 2, found 1. Consider adding 1 more type: `(i16, _)`
+ --> tests/compile_fail/from/struct_tuple_too_short.rs:2:8
+  |
+2 | #[from((i16,))]
+  |        ^^^^^^
diff --git a/tests/compile_fail/from/union.rs b/tests/compile_fail/from/union.rs
new file mode 100644
index 0000000..d0e78f1
--- /dev/null
+++ b/tests/compile_fail/from/union.rs
@@ -0,0 +1,6 @@
+#[derive(derive_more::From)]
+pub union Foo {
+    bar: i32,
+}
+
+fn main() {}
diff --git a/tests/compile_fail/from/union.stderr b/tests/compile_fail/from/union.stderr
new file mode 100644
index 0000000..1d6a44b
--- /dev/null
+++ b/tests/compile_fail/from/union.stderr
@@ -0,0 +1,5 @@
+error: `From` cannot be derived for unions
+ --> tests/compile_fail/from/union.rs:2:5
+  |
+2 | pub union Foo {
+  |     ^^^^^
diff --git a/tests/from.rs b/tests/from.rs
index f6b26c8..e43fa74 100644
--- a/tests/from.rs
+++ b/tests/from.rs
@@ -13,185 +13,1779 @@ use alloc::{
 use std::borrow::Cow;
 
 use derive_more::From;
+use static_assertions::assert_not_impl_any;
 
-#[derive(From)]
-struct EmptyTuple();
+mod structs {
+    use super::*;
 
-#[derive(From)]
-struct EmptyStruct {}
+    mod unit {
+        use super::*;
 
-#[derive(From)]
-struct EmptyUnit;
+        #[derive(Debug, From, PartialEq)]
+        struct Unit;
 
-#[derive(From)]
-struct MyInt(i32);
+        #[derive(Debug, From, PartialEq)]
+        struct Tuple();
 
-#[derive(From)]
-struct MyInts(i32, i32);
+        #[derive(Debug, From, PartialEq)]
+        struct Struct {}
 
-#[derive(From)]
-struct Point1D {
-    x: i32,
-}
+        #[test]
+        fn assert() {
+            assert_eq!(Unit, ().into());
+            assert_eq!(Tuple(), ().into());
+            assert_eq!(Struct {}, ().into());
+        }
 
-#[derive(From)]
-struct Point2D {
-    x: i32,
-    y: i32,
-}
+        mod generic {
+            use super::*;
 
-#[derive(From)]
-enum MixedInts {
-    SmallInt(i32),
-    NamedBigInt {
-        int: i64,
-    },
-    TwoSmallInts(i32, i32),
-    NamedBigInts {
-        x: i64,
-        y: i64,
-    },
-    #[from(ignore)]
-    Unsigned(u32),
-    NamedUnsigned {
-        x: u32,
-    },
-}
+            #[derive(Debug, From, PartialEq)]
+            struct Unit<const N: usize>;
 
-#[derive(Debug, Eq, PartialEq)]
-#[derive(From)]
-#[from(forward)]
-struct MyIntForward(u64);
+            #[derive(Debug, From, PartialEq)]
+            struct Tuple<const N: usize>();
 
-#[test]
-fn forward_struct() {
-    assert_eq!(MyIntForward(42), 42u32.into());
-    assert_eq!(MyIntForward(42), 42u16.into());
-    assert_eq!(MyIntForward(42), 42u64.into());
-}
+            #[derive(Debug, From, PartialEq)]
+            struct Struct<const N: usize> {}
 
-#[derive(Debug, Eq, PartialEq)]
-#[derive(From)]
-enum MixedIntsForward {
-    #[from(forward)]
-    SmallInt(i32),
-    NamedBigInt {
-        int: i64,
-    },
-}
+            #[test]
+            fn assert() {
+                assert_eq!(Unit::<1>, ().into());
+                assert_eq!(Tuple::<1>(), ().into());
+                assert_eq!(Struct::<1> {}, ().into());
+            }
+        }
+    }
 
-#[test]
-fn forward_enum() {
-    assert_eq!(MixedIntsForward::SmallInt(42), 42i32.into());
-    assert_eq!(MixedIntsForward::SmallInt(42), 42i16.into());
-}
+    mod single_field {
+        use super::*;
 
-#[derive(From, PartialEq)]
-enum AutoIgnore {
-    SmallInt(i32),
-    Uninteresting,
-    Uninteresting2,
-}
+        #[derive(Debug, From, PartialEq)]
+        struct Tuple(i32);
 
-#[test]
-fn auto_ignore_variants() {
-    assert!(AutoIgnore::SmallInt(42) == 42i32.into());
-}
+        #[derive(Debug, From, PartialEq)]
+        struct Struct {
+            field: i32,
+        }
 
-#[derive(From, PartialEq)]
-enum AutoIgnoreWithDefaultTrue {
-    #[from(ignore)]
-    SmallInt(i32),
-    Uninteresting,
-    Uninteresting2,
-}
+        #[test]
+        fn assert() {
+            assert_eq!(Tuple(42), 42.into());
+            assert_eq!(Struct { field: 42 }, 42.into());
+        }
 
-#[derive(From, PartialEq)]
-enum AutoIgnoreWithForwardFields2 {
-    #[from(forward)]
-    SmallInt(i32),
-    SmallIntIgnore(i32),
-}
+        mod types {
+            use super::*;
 
-#[test]
-fn auto_ignore_with_forward_field2() {
-    assert!(AutoIgnoreWithForwardFields2::SmallInt(42) == 42i32.into());
-    assert!(AutoIgnoreWithForwardFields2::SmallInt(42) == 42i16.into());
-}
+            #[derive(Debug, From, PartialEq)]
+            #[from(i8)]
+            #[from(i16)]
+            struct Tuple(i32);
 
-#[derive(Debug, Eq, PartialEq)]
-#[derive(From)]
-#[from(types(u8, u16, u32))]
-struct MyIntExplicit(u64);
-
-#[test]
-fn explicit_types_struct() {
-    assert_eq!(MyIntExplicit(42), 42u8.into());
-    assert_eq!(MyIntExplicit(42), 42u16.into());
-    assert_eq!(MyIntExplicit(42), 42u32.into());
-    assert_eq!(MyIntExplicit(42), 42u64.into());
-}
+            #[derive(Debug, From, PartialEq)]
+            #[from(&str, Cow<'_, str>)]
+            struct Struct {
+                field: String,
+            }
 
-#[derive(Debug, Eq, PartialEq)]
-#[derive(From)]
-#[from(types(i8, i16))]
-struct MyIntsExplicit(i32, i32);
+            #[test]
+            fn assert() {
+                assert_not_impl_any!(Tuple: From<i32>);
+                assert_not_impl_any!(Struct: From<String>);
 
-#[test]
-fn explicit_types_struct_tupled() {
-    assert_eq!(MyIntsExplicit(42, 42), (42i32, 42i32).into());
-    assert_eq!(MyIntsExplicit(42, 42), (42i8, 42i8).into());
-    assert_eq!(MyIntsExplicit(42, 42), (42i16, 42i16).into());
-}
+                assert_eq!(Tuple(42), 42_i8.into());
+                assert_eq!(Tuple(42), 42_i16.into());
+                assert_eq!(
+                    Struct {
+                        field: "42".to_string(),
+                    },
+                    "42".into(),
+                );
+                assert_eq!(
+                    Struct {
+                        field: "42".to_string(),
+                    },
+                    Cow::Borrowed("42").into(),
+                );
+            }
+        }
 
-#[derive(Debug, Eq, PartialEq)]
-#[derive(From)]
-enum MixedIntsExplicit {
-    #[from(types(i8))]
-    SmallInt(i32),
-    #[from(types(i16, i64))]
-    AnotherInt(i128),
-    NamedBigInt {
-        int: i64,
-    },
-}
+        mod forward {
+            use super::*;
 
-#[test]
-fn explicit_types_enum() {
-    assert_eq!(MixedIntsExplicit::SmallInt(42), 42i32.into());
-    assert_eq!(MixedIntsExplicit::SmallInt(42), 42i8.into());
+            #[derive(Debug, From, PartialEq)]
+            #[from(forward)]
+            struct Tuple(i32);
 
-    assert_eq!(MixedIntsExplicit::AnotherInt(42), 42i128.into());
-    assert_eq!(MixedIntsExplicit::AnotherInt(42), 42i64.into());
-    assert_eq!(MixedIntsExplicit::AnotherInt(42), 42i16.into());
-}
+            #[derive(Debug, From, PartialEq)]
+            #[from(forward)]
+            struct Struct {
+                field: String,
+            }
 
-#[derive(Debug, Eq, PartialEq)]
-#[derive(From)]
-#[from(types(i8, i16))]
-struct Point2DExplicit {
-    x: i32,
-    y: i32,
-}
+            #[test]
+            fn assert() {
+                assert_eq!(Tuple(42), 42_i8.into());
+                assert_eq!(Tuple(42), 42_i16.into());
+                assert_eq!(Tuple(42), 42_i32.into());
+                assert_eq!(
+                    Struct {
+                        field: "42".to_string(),
+                    },
+                    "42".into(),
+                );
+                assert_eq!(
+                    Struct {
+                        field: "42".to_string(),
+                    },
+                    Cow::Borrowed("42").into(),
+                );
+            }
+        }
+
+        mod generic {
+            use super::*;
+
+            #[derive(Debug, From, PartialEq)]
+            struct Tuple<T>(T);
+
+            #[derive(Debug, From, PartialEq)]
+            struct Struct<T> {
+                field: T,
+            }
+
+            #[test]
+            fn assert() {
+                assert_eq!(Tuple(42), 42.into());
+                assert_eq!(Struct { field: 42 }, 42.into());
+            }
+
+            mod reference {
+                use super::*;
+
+                #[derive(Debug, From, PartialEq)]
+                struct Tuple<'a, T>(&'a T);
+
+                #[derive(Debug, From, PartialEq)]
+                struct Struct<'a, T> {
+                    field: &'a T,
+                }
+
+                #[test]
+                fn assert() {
+                    assert_eq!(Tuple(&42), (&42).into());
+                    assert_eq!(Struct { field: &42 }, (&42).into());
+                }
+            }
+
+            mod indirect {
+                use super::*;
+
+                #[derive(Debug, From, PartialEq)]
+                struct Tuple<T: 'static>(&'static T);
+
+                #[derive(Debug, From, PartialEq)]
+                struct Struct<T: 'static> {
+                    field: &'static T,
+                }
+
+                #[test]
+                fn assert() {
+                    assert_eq!(Tuple(&42), (&42).into());
+                    assert_eq!(Struct { field: &42 }, (&42).into());
+                }
+            }
+
+            mod bounded {
+                use super::*;
+
+                #[derive(Debug, From, PartialEq)]
+                struct Tuple<T: Clone>(T);
+
+                #[derive(Debug, From, PartialEq)]
+                struct Struct<T: Clone> {
+                    field: T,
+                }
+
+                #[test]
+                fn assert() {
+                    assert_eq!(Tuple(42), 42.into());
+                    assert_eq!(Struct { field: 42 }, 42.into());
+                }
+            }
+
+            mod r#const {
+                use super::*;
+
+                #[derive(Debug, From, PartialEq)]
+                struct Tuple<const N: usize, T>(T);
+
+                #[derive(Debug, From, PartialEq)]
+                struct Struct<T, const N: usize> {
+                    field: T,
+                }
+
+                #[test]
+                fn assert() {
+                    assert_eq!(Tuple::<1, _>(1), 1.into());
+                    assert_eq!(Struct::<_, 1> { field: 1 }, 1.into());
+                }
+            }
+        }
+    }
+
+    mod multi_field {
+        use super::*;
+
+        #[derive(Debug, From, PartialEq)]
+        struct Tuple(i32, i16);
+
+        #[derive(Debug, From, PartialEq)]
+        struct Struct {
+            field1: i32,
+            field2: i16,
+        }
+
+        #[test]
+        fn assert() {
+            assert_eq!(Tuple(0, 1), (0, 1_i16).into());
+            assert_eq!(
+                Struct {
+                    field1: 0,
+                    field2: 1,
+                },
+                (0, 1_i16).into(),
+            );
+        }
+
+        mod types {
+            use super::*;
+
+            #[derive(Debug, From, PartialEq)]
+            #[from((i16, i16))]
+            struct Tuple(i32, i16);
+
+            #[derive(Debug, From, PartialEq)]
+            #[from((i16, i16))]
+            struct Struct {
+                field1: i32,
+                field2: i16,
+            }
+
+            #[test]
+            fn assert() {
+                assert_not_impl_any!(Tuple: From<(i32, i16)>);
+                assert_not_impl_any!(Struct: From<(i32, i16)>);
+
+                assert_eq!(Tuple(0, 1), (0_i16, 1_i16).into());
+                assert_eq!(
+                    Struct {
+                        field1: 0,
+                        field2: 1
+                    },
+                    (0_i16, 1_i16).into(),
+                );
+            }
+        }
+
+        mod forward {
+            use super::*;
+
+            #[derive(Debug, From, PartialEq)]
+            #[from(forward)]
+            struct Tuple(i32, i16);
+
+            #[derive(Debug, From, PartialEq)]
+            #[from(forward)]
+            struct Struct {
+                field1: i32,
+                field2: i16,
+            }
+
+            #[test]
+            fn assert() {
+                assert_eq!(Tuple(0, 1), (0_i8, 1_i8).into());
+                assert_eq!(Tuple(0, 1), (0_i8, 1_i16).into());
+                assert_eq!(Tuple(0, 1), (0_i16, 1_i8).into());
+                assert_eq!(Tuple(0, 1), (0_i16, 1_i16).into());
+                assert_eq!(Tuple(0, 1), (0_i32, 1_i8).into());
+                assert_eq!(Tuple(0, 1), (0_i32, 1_i16).into());
+                assert_eq!(
+                    Struct {
+                        field1: 0,
+                        field2: 1
+                    },
+                    (0_i8, 1_i8).into(),
+                );
+                assert_eq!(
+                    Struct {
+                        field1: 0,
+                        field2: 1
+                    },
+                    (0_i8, 1_i16).into(),
+                );
+                assert_eq!(
+                    Struct {
+                        field1: 0,
+                        field2: 1
+                    },
+                    (0_i16, 1_i8).into(),
+                );
+                assert_eq!(
+                    Struct {
+                        field1: 0,
+                        field2: 1
+                    },
+                    (0_i16, 1_i16).into(),
+                );
+                assert_eq!(
+                    Struct {
+                        field1: 0,
+                        field2: 1
+                    },
+                    (0_i32, 1_i8).into(),
+                );
+                assert_eq!(
+                    Struct {
+                        field1: 0,
+                        field2: 1
+                    },
+                    (0_i32, 1_i16).into(),
+                );
+            }
+        }
+
+        mod generic {
+            use super::*;
+
+            #[derive(Debug, From, PartialEq)]
+            struct Tuple<A, B>(A, B);
+
+            #[derive(Debug, From, PartialEq)]
+            struct Struct<A, B> {
+                field1: A,
+                field2: B,
+            }
+
+            #[test]
+            fn assert() {
+                assert_eq!(Tuple(1, 2_i8), (1, 2_i8).into());
+                assert_eq!(
+                    Struct {
+                        field1: 1,
+                        field2: 2_i8,
+                    },
+                    (1, 2_i8).into(),
+                );
+            }
+
+            mod reference {
+                use super::*;
+
+                #[derive(Debug, From, PartialEq)]
+                struct Tuple<'a, A, B>(&'a A, &'a B);
+
+                #[derive(Debug, From, PartialEq)]
+                struct Struct<'a, A, B> {
+                    field1: &'a A,
+                    field2: &'a B,
+                }
+
+                #[test]
+                fn assert() {
+                    assert_eq!(Tuple(&1, &2_i8), (&1, &2_i8).into());
+                    assert_eq!(
+                        Struct {
+                            field1: &1,
+                            field2: &2_i8,
+                        },
+                        (&1, &2_i8).into(),
+                    );
+                }
+            }
+
+            mod bounded {
+                use super::*;
+
+                #[derive(Debug, From, PartialEq)]
+                struct Tuple<A: Clone, B>(A, B);
+
+                #[derive(Debug, From, PartialEq)]
+                struct Struct<A: Clone, B> {
+                    field1: A,
+                    field2: B,
+                }
+
+                #[test]
+                fn assert() {
+                    assert_eq!(Tuple(1, 2_i8), (1, 2_i8).into());
+                    assert_eq!(
+                        Struct {
+                            field1: 1,
+                            field2: 2_i8,
+                        },
+                        (1, 2_i8).into(),
+                    );
+                }
+            }
 
-#[test]
-fn explicit_types_point_2d() {
-    let expected = Point2DExplicit { x: 42, y: 42 };
-    assert_eq!(expected, (42i32, 42i32).into());
-    assert_eq!(expected, (42i8, 42i8).into());
-    assert_eq!(expected, (42i16, 42i16).into());
+            mod r#const {
+                use super::*;
+
+                #[derive(Debug, From, PartialEq)]
+                struct ConstTuple<const N: usize, A, B>(A, B);
+
+                #[derive(Debug, From, PartialEq)]
+                struct ConstStruct<const N: usize, A, B> {
+                    field1: A,
+                    field2: B,
+                }
+
+                #[test]
+                fn assert() {
+                    assert_eq!(ConstTuple::<1, _, _>(1, 2_i8), (1, 2_i8).into());
+                    assert_eq!(
+                        ConstStruct::<1, _, _> {
+                            field1: 1,
+                            field2: 2_i8,
+                        },
+                        (1, 2_i8).into(),
+                    );
+                }
+            }
+        }
+    }
 }
 
-#[derive(Debug, Eq, PartialEq)]
-#[derive(From)]
-#[from(types("Cow<'_, str>", "&str"))]
-struct Name(String);
-
-#[test]
-fn explicit_complex_types_name() {
-    let name = "Eärendil";
-    let expected = Name(name.into());
-    assert_eq!(expected, name.to_string().into());
-    assert_eq!(expected, name.into());
-    assert_eq!(expected, Cow::Borrowed(name).into());
+mod enums {
+    use super::*;
+
+    mod unit_variant {
+        use super::*;
+
+        #[derive(Debug, From, PartialEq)]
+        enum Enum {
+            #[from]
+            Unit,
+            Unnamed(),
+            Named {},
+        }
+
+        #[test]
+        fn assert() {
+            assert_eq!(Enum::Unit, ().into());
+        }
+
+        mod generic {
+            use super::*;
+
+            #[derive(Debug, From, PartialEq)]
+            enum Unit {
+                Variant,
+            }
+
+            #[derive(Debug, From, PartialEq)]
+            enum Tuple {
+                Variant(),
+            }
+
+            #[derive(Debug, From, PartialEq)]
+            enum Struct {
+                Variant {},
+            }
+
+            assert_not_impl_any!(Unit: From<()>);
+            assert_not_impl_any!(Tuple: From<()>);
+            assert_not_impl_any!(Struct: From<()>);
+
+            mod r#const {
+                use super::*;
+
+                #[derive(Debug, From, PartialEq)]
+                enum Unit<const N: usize> {
+                    Variant,
+                }
+
+                #[derive(Debug, From, PartialEq)]
+                enum Tuple<const N: usize> {
+                    Variant(),
+                }
+
+                #[derive(Debug, From, PartialEq)]
+                enum Struct<const N: usize> {
+                    Variant {},
+                }
+
+                assert_not_impl_any!(Unit<0>: From<()>);
+                assert_not_impl_any!(Tuple<0>: From<()>);
+                assert_not_impl_any!(Struct<0>: From<()>);
+            }
+        }
+
+        mod from {
+            use super::*;
+
+            #[derive(Debug, From, PartialEq)]
+            enum Unit {
+                #[from]
+                Variant,
+                AutomaticallySkipped,
+            }
+
+            #[derive(Debug, From, PartialEq)]
+            enum Tuple {
+                #[from]
+                Variant(),
+                AutomaticallySkipped(),
+            }
+
+            #[derive(Debug, From, PartialEq)]
+            enum Struct {
+                #[from]
+                Variant {},
+                AutomaticallySkipped {},
+            }
+
+            #[test]
+            fn assert() {
+                assert_eq!(Unit::Variant, ().into());
+                assert_eq!(Tuple::Variant(), ().into());
+                assert_eq!(Struct::Variant {}, ().into());
+            }
+
+            mod r#const {
+                use super::*;
+
+                #[derive(Debug, From, PartialEq)]
+                enum Unit<const N: usize> {
+                    #[from]
+                    Variant,
+                }
+
+                #[derive(Debug, From, PartialEq)]
+                enum Tuple<const N: usize> {
+                    #[from]
+                    Variant(),
+                }
+
+                #[derive(Debug, From, PartialEq)]
+                enum Struct<const N: usize> {
+                    #[from]
+                    Variant {},
+                }
+
+                #[test]
+                fn assert() {
+                    assert_eq!(Unit::<0>::Variant, ().into());
+                    assert_eq!(Tuple::<0>::Variant(), ().into());
+                    assert_eq!(Struct::<0>::Variant {}, ().into());
+                }
+            }
+        }
+    }
+
+    mod single_field_variant {
+        use super::*;
+
+        #[derive(Debug, From, PartialEq)]
+        enum Enum {
+            Unnamed(i8),
+            Named { field: i16 },
+        }
+
+        #[test]
+        fn assert() {
+            assert_eq!(Enum::Unnamed(1), 1_i8.into());
+            assert_eq!(Enum::Named { field: 1 }, 1_i16.into());
+        }
+
+        mod generic {
+            use super::*;
+
+            #[derive(Debug, From, PartialEq)]
+            enum Tuple<T> {
+                Variant(T),
+            }
+
+            #[derive(Debug, From, PartialEq)]
+            enum Struct<T> {
+                Variant { field: T },
+            }
+
+            #[test]
+            fn assert() {
+                assert_eq!(Tuple::Variant(1), 1.into());
+                assert_eq!(Struct::Variant { field: 1 }, 1.into());
+            }
+
+            mod reference {
+                use super::*;
+
+                #[derive(Debug, From, PartialEq)]
+                enum Tuple<'a, T> {
+                    Variant(&'a T),
+                }
+
+                #[derive(Debug, From, PartialEq)]
+                enum Struct<'a, T> {
+                    Variant { field: &'a T },
+                }
+
+                #[test]
+                fn assert() {
+                    assert_eq!(Tuple::Variant(&1), (&1).into());
+                    assert_eq!(Struct::Variant { field: &1 }, (&1).into());
+                }
+            }
+
+            mod indirect {
+                use super::*;
+
+                #[derive(Debug, From, PartialEq)]
+                enum Tuple<T: 'static> {
+                    Variant(&'static T),
+                }
+
+                #[derive(Debug, From, PartialEq)]
+                enum Struct<T: 'static> {
+                    Variant { field: &'static T },
+                }
+
+                #[test]
+                fn assert() {
+                    assert_eq!(Tuple::Variant(&1), (&1).into());
+                    assert_eq!(Struct::Variant { field: &1 }, (&1).into());
+                }
+            }
+
+            mod bounded {
+                use super::*;
+
+                #[derive(Debug, From, PartialEq)]
+                enum Tuple<T: Clone> {
+                    Variant(T),
+                }
+
+                #[derive(Debug, From, PartialEq)]
+                enum Struct<T: Clone> {
+                    Variant { field: T },
+                }
+
+                #[test]
+                fn assert() {
+                    assert_eq!(Tuple::Variant(1), 1.into());
+                    assert_eq!(Struct::Variant { field: 1 }, 1.into());
+                }
+            }
+
+            mod r#const {
+                use super::*;
+
+                #[derive(Debug, From, PartialEq)]
+                enum Tuple<T, const N: usize> {
+                    Variant(T),
+                }
+
+                #[derive(Debug, From, PartialEq)]
+                enum Struct<const N: usize, T> {
+                    Variant { field: T },
+                }
+
+                #[test]
+                fn assert() {
+                    assert_eq!(Tuple::Variant::<_, 1>(1), 1.into());
+                    assert_eq!(Struct::Variant::<1, _> { field: 1 }, 1.into());
+                }
+            }
+        }
+
+        mod from {
+            use super::*;
+
+            #[derive(Debug, From, PartialEq)]
+            enum Tuple {
+                #[from]
+                Variant(i8),
+                AutomaticallySkipped(i8),
+            }
+
+            #[derive(Debug, From, PartialEq)]
+            enum Struct {
+                #[from]
+                Variant {
+                    field: i8,
+                },
+                AutomaticallySkipped {
+                    field: i8,
+                },
+            }
+
+            #[test]
+            fn assert() {
+                assert_eq!(Tuple::Variant(1), 1_i8.into());
+                assert_eq!(Struct::Variant { field: 1 }, 1_i8.into());
+            }
+        }
+
+        mod skip {
+            use super::*;
+
+            #[derive(Debug, From, PartialEq)]
+            enum Enum {
+                Unnamed(i8),
+                #[from(skip)]
+                UnnamedSkipped(i8),
+                Named {
+                    field: i16,
+                },
+                #[from(skip)]
+                NamedSkipped {
+                    field: i16,
+                },
+            }
+
+            #[test]
+            fn assert() {
+                assert_eq!(Enum::Unnamed(1), 1_i8.into());
+                assert_eq!(Enum::Named { field: 1 }, 1_i16.into());
+            }
+
+            mod generic {
+                use super::*;
+
+                #[derive(Debug, From, PartialEq)]
+                enum Tuple<T> {
+                    Variant(T),
+                    #[from(skip)]
+                    Skipped(T),
+                }
+
+                #[derive(Debug, From, PartialEq)]
+                enum Struct<T> {
+                    Variant {
+                        field: T,
+                    },
+                    #[from(skip)]
+                    Skipped {
+                        field: T,
+                    },
+                }
+
+                #[test]
+                fn assert() {
+                    assert_eq!(Tuple::Variant(1), 1.into());
+                    assert_eq!(Struct::Variant { field: 1 }, 1.into());
+                }
+
+                mod reference {
+                    use super::*;
+
+                    #[derive(Debug, From, PartialEq)]
+                    enum Tuple<'a, T> {
+                        Variant(&'a T),
+                        #[from(skip)]
+                        Skipped(&'a T),
+                    }
+
+                    #[derive(Debug, From, PartialEq)]
+                    enum Struct<'a, T> {
+                        Variant {
+                            field: &'a T,
+                        },
+                        #[from(skip)]
+                        Skipped {
+                            field: &'a T,
+                        },
+                    }
+
+                    #[test]
+                    fn assert() {
+                        assert_eq!(Tuple::Variant(&1), (&1).into());
+                        assert_eq!(Struct::Variant { field: &1 }, (&1).into());
+                    }
+                }
+
+                mod indirect {
+                    use super::*;
+
+                    #[derive(Debug, From, PartialEq)]
+                    enum Tuple<T: 'static> {
+                        Variant(&'static T),
+                        #[from(skip)]
+                        Skipped(&'static T),
+                    }
+
+                    #[derive(Debug, From, PartialEq)]
+                    enum Struct<T: 'static> {
+                        Variant {
+                            field: &'static T,
+                        },
+                        #[from(skip)]
+                        Skipped {
+                            field: &'static T,
+                        },
+                    }
+
+                    #[test]
+                    fn assert() {
+                        assert_eq!(Tuple::Variant(&1), (&1).into());
+                        assert_eq!(Struct::Variant { field: &1 }, (&1).into());
+                    }
+                }
+
+                mod bounded {
+                    use super::*;
+
+                    #[derive(Debug, From, PartialEq)]
+                    enum Tuple<T: Clone> {
+                        Variant(T),
+                        #[from(skip)]
+                        Skipped(T),
+                    }
+
+                    #[derive(Debug, From, PartialEq)]
+                    enum Struct<T: Clone> {
+                        Variant {
+                            field: T,
+                        },
+                        #[from(skip)]
+                        Skipped {
+                            field: T,
+                        },
+                    }
+
+                    #[test]
+                    fn assert() {
+                        assert_eq!(Tuple::Variant(1), 1.into());
+                        assert_eq!(Struct::Variant { field: 1 }, 1.into());
+                    }
+                }
+
+                mod r#const {
+                    use super::*;
+
+                    #[derive(Debug, From, PartialEq)]
+                    enum Tuple<T, const N: usize> {
+                        Variant(T),
+                        #[from(skip)]
+                        Skipped(T),
+                    }
+
+                    #[derive(Debug, From, PartialEq)]
+                    enum Struct<const N: usize, T> {
+                        Variant {
+                            field: T,
+                        },
+                        #[from(skip)]
+                        Skipped {
+                            field: T,
+                        },
+                    }
+
+                    #[test]
+                    fn assert() {
+                        assert_eq!(Tuple::Variant::<_, 1>(1), 1.into());
+                        assert_eq!(Struct::Variant::<1, _> { field: 1 }, 1.into());
+                    }
+                }
+
+                mod concrete {
+                    use super::*;
+
+                    #[derive(Debug, From, PartialEq)]
+                    enum Tuple<T> {
+                        Variant(i32),
+                        #[from(skip)]
+                        Skipped(T),
+                    }
+
+                    #[derive(Debug, From, PartialEq)]
+                    enum Struct<T> {
+                        Variant {
+                            field: i32,
+                        },
+                        #[from(skip)]
+                        Skipped {
+                            field: T,
+                        },
+                    }
+
+                    #[test]
+                    fn assert() {
+                        assert_eq!(Tuple::Variant::<i32>(1), 1.into());
+                        assert_eq!(Struct::Variant::<i32> { field: 1 }, 1.into());
+                    }
+
+                    mod reference {
+                        use super::*;
+
+                        #[derive(Debug, From, PartialEq)]
+                        enum Tuple<'a, T> {
+                            Variant(&'a i32),
+                            #[from(skip)]
+                            Skipped(&'a T),
+                        }
+
+                        #[derive(Debug, From, PartialEq)]
+                        enum Struct<'a, T> {
+                            Variant {
+                                field: &'a i32,
+                            },
+                            #[from(skip)]
+                            Skipped {
+                                field: &'a T,
+                            },
+                        }
+
+                        #[test]
+                        fn assert() {
+                            assert_eq!(Tuple::Variant::<i32>(&1), (&1).into());
+                            assert_eq!(
+                                Struct::Variant::<i32> { field: &1 },
+                                (&1).into()
+                            );
+                        }
+                    }
+
+                    mod indirect {
+                        use super::*;
+
+                        #[derive(Debug, From, PartialEq)]
+                        enum Tuple<T: 'static> {
+                            Variant(&'static i32),
+                            #[from(skip)]
+                            Skipped(&'static T),
+                        }
+
+                        #[derive(Debug, From, PartialEq)]
+                        enum Struct<T: 'static> {
+                            Variant {
+                                field: &'static i32,
+                            },
+                            #[from(skip)]
+                            Skipped {
+                                field: &'static T,
+                            },
+                        }
+
+                        #[test]
+                        fn assert() {
+                            assert_eq!(Tuple::Variant::<i32>(&1), (&1).into());
+                            assert_eq!(
+                                Struct::Variant::<i32> { field: &1 },
+                                (&1).into()
+                            );
+                        }
+                    }
+
+                    mod bounded {
+                        use super::*;
+
+                        #[derive(Debug, From, PartialEq)]
+                        enum Tuple<T: Clone> {
+                            Variant(i32),
+                            #[from(skip)]
+                            Skipped(T),
+                        }
+
+                        #[derive(Debug, From, PartialEq)]
+                        enum Struct<T: Clone> {
+                            Variant {
+                                field: i32,
+                            },
+                            #[from(skip)]
+                            Skipped {
+                                field: T,
+                            },
+                        }
+
+                        #[test]
+                        fn assert() {
+                            assert_eq!(Tuple::Variant::<i32>(1), 1.into());
+                            assert_eq!(Struct::Variant::<i32> { field: 1 }, 1.into());
+                        }
+                    }
+
+                    mod r#const {
+                        use super::*;
+
+                        #[derive(Debug, From, PartialEq)]
+                        enum Tuple<T, const N: usize> {
+                            Variant(i32),
+                            #[from(skip)]
+                            Skipped(T),
+                        }
+
+                        #[derive(Debug, From, PartialEq)]
+                        enum Struct<const N: usize, T> {
+                            Variant {
+                                field: i32,
+                            },
+                            #[from(skip)]
+                            Skipped {
+                                field: T,
+                            },
+                        }
+
+                        #[test]
+                        fn assert() {
+                            assert_eq!(Tuple::Variant::<i32, 1>(1), 1.into());
+                            assert_eq!(
+                                Struct::Variant::<1, i32> { field: 1 },
+                                1.into()
+                            );
+                        }
+                    }
+                }
+            }
+        }
+
+        mod types {
+            use super::*;
+
+            #[derive(Debug, From, PartialEq)]
+            enum Enum {
+                #[from(i8)]
+                Unnamed(i16),
+                #[from(i16)]
+                Named {
+                    field: i32,
+                },
+                AutomaticallySkipped(i32),
+            }
+
+            #[test]
+            fn assert() {
+                assert_not_impl_any!(Enum: From<i32>);
+                assert_eq!(Enum::Unnamed(1), 1_i8.into());
+                assert_eq!(Enum::Named { field: 1 }, 1_i16.into());
+            }
+        }
+
+        mod forward {
+            use super::*;
+
+            #[derive(Debug, From, PartialEq)]
+            enum Unnamed {
+                #[from(forward)]
+                Variant(i32),
+                AutomaticallyIgnored(i32),
+            }
+
+            #[derive(Debug, From, PartialEq)]
+            enum Named {
+                #[from(forward)]
+                Variant {
+                    field: i32,
+                },
+                AutomaticallyIgnored {
+                    field: i32,
+                },
+            }
+
+            #[test]
+            fn assert() {
+                assert_eq!(Unnamed::Variant(1), 1_i8.into());
+                assert_eq!(Unnamed::Variant(1), 1_i16.into());
+                assert_eq!(Unnamed::Variant(1), 1_i32.into());
+                assert_eq!(Named::Variant { field: 1 }, 1_i8.into());
+                assert_eq!(Named::Variant { field: 1 }, 1_i16.into());
+                assert_eq!(Named::Variant { field: 1 }, 1_i32.into());
+            }
+        }
+    }
+
+    mod multi_field_variant {
+        use super::*;
+
+        #[derive(Debug, From, PartialEq)]
+        enum Enum {
+            Tuple(i8, i8),
+            Struct { field1: i16, field2: i16 },
+        }
+
+        #[test]
+        fn assert() {
+            assert_eq!(Enum::Tuple(0, 1), (0_i8, 1_i8).into());
+            assert_eq!(
+                Enum::Struct {
+                    field1: 0,
+                    field2: 1
+                },
+                (0_i16, 1_i16).into(),
+            );
+        }
+
+        mod generic {
+            use super::*;
+
+            #[derive(Debug, From, PartialEq)]
+            enum Tuple<A, B> {
+                Variant(A, B),
+            }
+
+            #[derive(Debug, From, PartialEq)]
+            enum Struct<A, B> {
+                Variant { field1: A, field2: B },
+            }
+
+            #[test]
+            fn assert() {
+                assert_eq!(Tuple::Variant(1, 2_i16), (1, 2_i16).into());
+                assert_eq!(
+                    Struct::Variant {
+                        field1: 1,
+                        field2: 2_i16,
+                    },
+                    (1, 2_i16).into(),
+                );
+            }
+
+            mod reference {
+                use super::*;
+
+                #[derive(Debug, From, PartialEq)]
+                enum Tuple<'a, A, B> {
+                    Variant(&'a A, &'a B),
+                }
+
+                #[derive(Debug, From, PartialEq)]
+                enum Struct<'a, A, B> {
+                    Variant { field1: &'a A, field2: &'a B },
+                }
+
+                #[test]
+                fn assert() {
+                    assert_eq!(Tuple::Variant(&1, &2_i16), (&1, &2_i16).into());
+                    assert_eq!(
+                        Struct::Variant {
+                            field1: &1,
+                            field2: &2_i16,
+                        },
+                        (&1, &2_i16).into(),
+                    );
+                }
+            }
+
+            mod indirect {
+                use super::*;
+
+                #[derive(Debug, From, PartialEq)]
+                enum Tuple<A: 'static, B: 'static> {
+                    Variant(&'static A, &'static B),
+                }
+
+                #[derive(Debug, From, PartialEq)]
+                enum Struct<A: 'static, B: 'static> {
+                    Variant {
+                        field1: &'static A,
+                        field2: &'static B,
+                    },
+                }
+
+                #[test]
+                fn assert() {
+                    assert_eq!(Tuple::Variant(&1, &2_i16), (&1, &2_i16).into());
+                    assert_eq!(
+                        Struct::Variant {
+                            field1: &1,
+                            field2: &2_i16,
+                        },
+                        (&1, &2_i16).into(),
+                    );
+                }
+            }
+
+            mod bounded {
+                use super::*;
+
+                #[derive(Debug, From, PartialEq)]
+                enum Tuple<A: Clone, B> {
+                    Variant(A, B),
+                }
+
+                #[derive(Debug, From, PartialEq)]
+                enum Struct<A, B: Clone> {
+                    Variant { field1: A, field2: B },
+                }
+
+                #[test]
+                fn assert() {
+                    assert_eq!(Tuple::Variant(1, 2_i16), (1, 2_i16).into());
+                    assert_eq!(
+                        Struct::Variant {
+                            field1: 1,
+                            field2: 2_i16,
+                        },
+                        (1, 2_i16).into(),
+                    );
+                }
+            }
+
+            mod r#const {
+                use super::*;
+
+                #[derive(Debug, From, PartialEq)]
+                enum Tuple<const N: usize, A, B> {
+                    Variant(A, B),
+                }
+
+                #[derive(Debug, From, PartialEq)]
+                enum Struct<A, const N: usize, B> {
+                    Variant { field1: A, field2: B },
+                }
+
+                #[test]
+                fn assert() {
+                    assert_eq!(Tuple::Variant::<0, _, _>(1, 2_i16), (1, 2_i16).into());
+                    assert_eq!(
+                        Struct::<_, 1, _>::Variant {
+                            field1: 1,
+                            field2: 2_i16,
+                        },
+                        (1, 2_i16).into(),
+                    );
+                }
+            }
+        }
+
+        mod from {
+            use super::*;
+
+            #[derive(Debug, From, PartialEq)]
+            enum Tuple {
+                #[from]
+                Variant(i8, i16),
+                AutomaticallySkipped(i8, i16),
+            }
+
+            #[derive(Debug, From, PartialEq)]
+            enum Struct {
+                #[from]
+                Variant {
+                    field1: i8,
+                    field2: i16,
+                },
+                AutomaticallySkipped {
+                    field1: i8,
+                    field2: i16,
+                },
+            }
+
+            #[test]
+            fn assert() {
+                assert_eq!(Tuple::Variant(1, 2), (1_i8, 2_i16).into());
+                assert_eq!(
+                    Struct::Variant {
+                        field1: 1,
+                        field2: 2,
+                    },
+                    (1_i8, 2_i16).into(),
+                );
+            }
+        }
+
+        mod skip {
+            use super::*;
+
+            #[derive(Debug, From, PartialEq)]
+            enum Enum {
+                Tuple(i8, i8),
+                #[from(skip)]
+                TupleSkipped(i8, i8),
+                Struct {
+                    field1: i16,
+                    field2: i16,
+                },
+                #[from(skip)]
+                StructSkipped {
+                    field1: i16,
+                    field2: i16,
+                },
+            }
+
+            #[test]
+            fn assert() {
+                assert_eq!(Enum::Tuple(0, 1), (0_i8, 1_i8).into());
+                assert_eq!(
+                    Enum::Struct {
+                        field1: 0,
+                        field2: 1
+                    },
+                    (0_i16, 1_i16).into(),
+                );
+            }
+
+            mod generic {
+                use super::*;
+
+                #[derive(Debug, From, PartialEq)]
+                enum Tuple<A, B> {
+                    Variant(A, B),
+                    #[from(skip)]
+                    Skipped(A, B),
+                }
+
+                #[derive(Debug, From, PartialEq)]
+                enum Struct<A, B> {
+                    Variant {
+                        field1: A,
+                        field2: B,
+                    },
+                    #[from(skip)]
+                    Skipped {
+                        field1: A,
+                        field2: B,
+                    },
+                }
+
+                #[test]
+                fn assert() {
+                    assert_eq!(Tuple::Variant(1, 2_i16), (1, 2_i16).into());
+                    assert_eq!(
+                        Struct::Variant {
+                            field1: 1,
+                            field2: 2_i16,
+                        },
+                        (1, 2_i16).into(),
+                    );
+                }
+
+                mod reference {
+                    use super::*;
+
+                    #[derive(Debug, From, PartialEq)]
+                    enum Tuple<'a, A, B> {
+                        Variant(&'a A, &'a B),
+                        #[from(skip)]
+                        Skipped(&'a A, &'a B),
+                    }
+
+                    #[derive(Debug, From, PartialEq)]
+                    enum Struct<'a, A, B> {
+                        Variant {
+                            field1: &'a A,
+                            field2: &'a B,
+                        },
+                        #[from(skip)]
+                        Skipped {
+                            field1: &'a A,
+                            field2: &'a B,
+                        },
+                    }
+
+                    #[test]
+                    fn assert() {
+                        assert_eq!(Tuple::Variant(&1, &2_i16), (&1, &2_i16).into());
+                        assert_eq!(
+                            Struct::Variant {
+                                field1: &1,
+                                field2: &2_i16,
+                            },
+                            (&1, &2_i16).into(),
+                        );
+                    }
+                }
+
+                mod indirect {
+                    use super::*;
+
+                    #[derive(Debug, From, PartialEq)]
+                    enum Tuple<A: 'static, B: 'static> {
+                        Variant(&'static A, &'static B),
+                        #[from(skip)]
+                        Skipped(&'static A, &'static B),
+                    }
+
+                    #[derive(Debug, From, PartialEq)]
+                    enum Struct<A: 'static, B: 'static> {
+                        Variant {
+                            field1: &'static A,
+                            field2: &'static B,
+                        },
+                        #[from(skip)]
+                        Skipped {
+                            field1: &'static A,
+                            field2: &'static B,
+                        },
+                    }
+
+                    #[test]
+                    fn assert() {
+                        assert_eq!(Tuple::Variant(&1, &2_i16), (&1, &2_i16).into());
+                        assert_eq!(
+                            Struct::Variant {
+                                field1: &1,
+                                field2: &2_i16,
+                            },
+                            (&1, &2_i16).into(),
+                        );
+                    }
+                }
+
+                mod bounded {
+                    use super::*;
+
+                    #[derive(Debug, From, PartialEq)]
+                    enum Tuple<A: Clone, B> {
+                        Variant(A, B),
+                        #[from(skip)]
+                        Skipped(A, B),
+                    }
+
+                    #[derive(Debug, From, PartialEq)]
+                    enum Struct<A, B: Clone> {
+                        Variant {
+                            field1: A,
+                            field2: B,
+                        },
+                        #[from(skip)]
+                        Skipped {
+                            field1: A,
+                            field2: B,
+                        },
+                    }
+
+                    #[test]
+                    fn assert() {
+                        assert_eq!(Tuple::Variant(1, 2_i16), (1, 2_i16).into());
+                        assert_eq!(
+                            Struct::Variant {
+                                field1: 1,
+                                field2: 2_i16,
+                            },
+                            (1, 2_i16).into(),
+                        );
+                    }
+                }
+
+                mod r#const {
+                    use super::*;
+
+                    #[derive(Debug, From, PartialEq)]
+                    enum Tuple<const N: usize, A, B> {
+                        Variant(A, B),
+                        #[from(skip)]
+                        Skipped(A, B),
+                    }
+
+                    #[derive(Debug, From, PartialEq)]
+                    enum Struct<A, const N: usize, B> {
+                        Variant {
+                            field1: A,
+                            field2: B,
+                        },
+                        #[from(skip)]
+                        Skipped {
+                            field1: A,
+                            field2: B,
+                        },
+                    }
+
+                    #[test]
+                    fn assert() {
+                        assert_eq!(
+                            Tuple::Variant::<0, _, _>(1, 2_i16),
+                            (1, 2_i16).into()
+                        );
+                        assert_eq!(
+                            Struct::<_, 1, _>::Variant {
+                                field1: 1,
+                                field2: 2_i16,
+                            },
+                            (1, 2_i16).into(),
+                        );
+                    }
+                }
+
+                mod concrete {
+                    use super::*;
+
+                    #[derive(Debug, From, PartialEq)]
+                    enum Tuple<A, B> {
+                        Variant(i32, i16),
+                        #[from(skip)]
+                        Skipped(A, B),
+                    }
+
+                    #[derive(Debug, From, PartialEq)]
+                    enum Struct<A, B> {
+                        Variant {
+                            field1: i32,
+                            field2: i16,
+                        },
+                        #[from(skip)]
+                        Skipped {
+                            field1: A,
+                            field2: B,
+                        },
+                    }
+
+                    #[test]
+                    fn assert() {
+                        assert_eq!(
+                            Tuple::Variant::<i32, i16>(1, 2_i16),
+                            (1, 2_i16).into(),
+                        );
+                        assert_eq!(
+                            Struct::Variant::<i32, i16> {
+                                field1: 1,
+                                field2: 2_i16,
+                            },
+                            (1, 2_i16).into(),
+                        );
+                    }
+
+                    mod reference {
+                        use super::*;
+
+                        #[derive(Debug, From, PartialEq)]
+                        enum Tuple<'a, A, B> {
+                            Variant(&'a i32, &'a i16),
+                            #[from(skip)]
+                            Skipped(&'a A, &'a B),
+                        }
+
+                        #[derive(Debug, From, PartialEq)]
+                        enum Struct<'a, A, B> {
+                            Variant {
+                                field1: &'a i32,
+                                field2: &'a i16,
+                            },
+                            #[from(skip)]
+                            Skipped {
+                                field1: &'a A,
+                                field2: &'a B,
+                            },
+                        }
+
+                        #[test]
+                        fn assert() {
+                            assert_eq!(
+                                Tuple::Variant::<i32, i16>(&1, &2_i16),
+                                (&1, &2_i16).into(),
+                            );
+                            assert_eq!(
+                                Struct::Variant::<i32, i16> {
+                                    field1: &1,
+                                    field2: &2_i16,
+                                },
+                                (&1, &2_i16).into(),
+                            );
+                        }
+                    }
+
+                    mod indirect {
+                        use super::*;
+
+                        #[derive(Debug, From, PartialEq)]
+                        enum Tuple<A: 'static, B: 'static> {
+                            Variant(&'static i32, &'static i16),
+                            #[from(skip)]
+                            Skipped(&'static A, &'static B),
+                        }
+
+                        #[derive(Debug, From, PartialEq)]
+                        enum Struct<A: 'static, B: 'static> {
+                            Variant {
+                                field1: &'static i32,
+                                field2: &'static i16,
+                            },
+                            #[from(skip)]
+                            Skipped {
+                                field1: &'static A,
+                                field2: &'static B,
+                            },
+                        }
+
+                        #[test]
+                        fn assert() {
+                            assert_eq!(
+                                Tuple::Variant::<i32, i16>(&1, &2_i16),
+                                (&1, &2_i16).into(),
+                            );
+                            assert_eq!(
+                                Struct::Variant::<i32, i16> {
+                                    field1: &1,
+                                    field2: &2_i16,
+                                },
+                                (&1, &2_i16).into(),
+                            );
+                        }
+                    }
+
+                    mod bounded {
+                        use super::*;
+
+                        #[derive(Debug, From, PartialEq)]
+                        enum Tuple<A: Clone, B> {
+                            Variant(i32, i16),
+                            #[from(skip)]
+                            Skipped(A, B),
+                        }
+
+                        #[derive(Debug, From, PartialEq)]
+                        enum Struct<A, B: Clone> {
+                            Variant {
+                                field1: i32,
+                                field2: i16,
+                            },
+                            #[from(skip)]
+                            Skipped {
+                                field1: A,
+                                field2: B,
+                            },
+                        }
+
+                        #[test]
+                        fn assert() {
+                            assert_eq!(
+                                Tuple::Variant::<i32, i16>(1, 2_i16),
+                                (1, 2_i16).into(),
+                            );
+                            assert_eq!(
+                                Struct::Variant::<i32, i16> {
+                                    field1: 1,
+                                    field2: 2_i16,
+                                },
+                                (1, 2_i16).into(),
+                            );
+                        }
+                    }
+
+                    mod r#const {
+                        use super::*;
+
+                        #[derive(Debug, From, PartialEq)]
+                        enum Tuple<const N: usize, A, B> {
+                            Variant(i32, i16),
+                            #[from(skip)]
+                            Skipped(A, B),
+                        }
+
+                        #[derive(Debug, From, PartialEq)]
+                        enum Struct<A, const N: usize, B> {
+                            Variant {
+                                field1: i32,
+                                field2: i16,
+                            },
+                            #[from(skip)]
+                            Skipped {
+                                field1: A,
+                                field2: B,
+                            },
+                        }
+
+                        #[test]
+                        fn assert() {
+                            assert_eq!(
+                                Tuple::Variant::<0, i32, i16>(1, 2_i16),
+                                (1, 2_i16).into()
+                            );
+                            assert_eq!(
+                                Struct::<i32, 1, i16>::Variant {
+                                    field1: 1,
+                                    field2: 2_i16,
+                                },
+                                (1, 2_i16).into(),
+                            );
+                        }
+                    }
+                }
+            }
+        }
+
+        mod types {
+            use super::*;
+
+            #[derive(Debug, From, PartialEq)]
+            enum Enum {
+                #[from((i8, i8))]
+                Tuple(i16, i16),
+                #[from((i16, i16))]
+                Struct {
+                    field1: i32,
+                    field2: i32,
+                },
+                AutomaticallySkipped {
+                    field1: i32,
+                    field2: i32,
+                },
+            }
+
+            #[test]
+            fn assert() {
+                assert_not_impl_any!(Enum: From<(i32, i32)>);
+                assert_eq!(Enum::Tuple(0, 1), (0_i8, 1_i8).into());
+                assert_eq!(
+                    Enum::Struct {
+                        field1: 0,
+                        field2: 1
+                    },
+                    (0_i16, 1_i16).into(),
+                );
+            }
+        }
+
+        mod forward {
+            use super::*;
+
+            #[derive(Debug, From, PartialEq)]
+            enum Unnamed {
+                #[from(forward)]
+                Variant(i16, i16),
+                AutomaticallyIgnored(i16, i16),
+            }
+
+            #[derive(Debug, From, PartialEq)]
+            enum Named {
+                #[from(forward)]
+                Variant {
+                    field1: i16,
+                    field2: i16,
+                },
+                AutomaticallyIgnored {
+                    field1: i16,
+                    field2: i16,
+                },
+            }
+
+            #[test]
+            fn assert() {
+                assert_eq!(Unnamed::Variant(0, 1), (0_i8, 1_i8).into());
+                assert_eq!(Unnamed::Variant(0, 1), (0_i8, 1_i16).into());
+                assert_eq!(Unnamed::Variant(0, 1), (0_i16, 1_i8).into());
+                assert_eq!(Unnamed::Variant(0, 1), (0_i16, 1_i16).into());
+                assert_eq!(
+                    Named::Variant {
+                        field1: 0,
+                        field2: 1
+                    },
+                    (0_i8, 1_i8).into(),
+                );
+                assert_eq!(
+                    Named::Variant {
+                        field1: 0,
+                        field2: 1
+                    },
+                    (0_i8, 1_i16).into(),
+                );
+                assert_eq!(
+                    Named::Variant {
+                        field1: 0,
+                        field2: 1
+                    },
+                    (0_i16, 1_i8).into(),
+                );
+                assert_eq!(
+                    Named::Variant {
+                        field1: 0,
+                        field2: 1
+                    },
+                    (0_i16, 1_i16).into(),
+                );
+            }
+        }
+    }
 }