Repository: JelteF/derive_more (rust)

Issue:
Optional source for Error
I’d like to be able to use `derive_more` with optional sources, like:

```rs
#[derive(derive_more::Error, derive_more::Display, Debug)]
#[display("it’s an error")]
struct MyErr {
    source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
}
```

Notably, this does work with `thiserror`.

At the moment this gives:

```
error[E0599]: the method `as_dyn_error` exists for enum `Option<Box<dyn Error + Send + Sync>>`, but its trait bounds were not satisfied
    |
187 | #[derive(derive_more::Error, derive_more::Display, Debug)]
    |          ^^^^^^^^^^^^^^^^^^
    |
571 | pub enum Option<T> {
    | ------------------ doesn't satisfy `_: AsDynError<'_>` or `_: Error`
    |
    = note: the following trait bounds were not satisfied:
            `std::option::Option<std::boxed::Box<dyn derive_more::Error + std::marker::Send + std::marker::Sync>>: derive_more::Error`
            which is required by `std::option::Option<std::boxed::Box<dyn derive_more::Error + std::marker::Send + std::marker::Sync>>: derive_more::__private::AsDynError<'_>`
note: the method `as_dyn_error` exists on the type `std::boxed::Box<(dyn derive_more::Error + std::marker::Send + std::marker::Sync + 'static)>`
    |
9   |     fn as_dyn_error(&self) -> &(dyn Error + 'a);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: this error originates in the derive macro `derive_more::Error` (in Nightly builds, run with -Z macro-backtrace for more info)
```

Pull request description:
Support optional `Error::source` (#426)
Resolves #426

## Synopsis

See https://github.com/JelteF/derive_more/issues/426#issue-2688714756:
> I’d like to be able to use `derive_more` with optional sources, like:
> ```rust
> #[derive(derive_more::Error, derive_more::Display, Debug)]
> #[display("it’s an error")]
> struct MyErr {
>     source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
> }
> ```

## Solution

The way, the `thiserror` does it, is by [checking](https://docs.rs/thiserror-impl/2.0.12/src/thiserror_impl/expand.rs.html#50) the [type name to contain `Option`](https://docs.rs/thiserror-impl/2.0.12/src/thiserror_impl/expand.rs.html#559-583). Relying on type could be fragile, as the call site could introduce its own type named `Option` in the scope where macro is called. Ideally, we need to aid this case with type machinery.

For supporting this via type machinery, we obviously need some sort of specialization to specialize case `Option<E: Error>` over `E: Error`, since vanilla Rust coherence naturally hits the wall of "upstream crates may add a new impl of trait `std::error::Error` for type `Option<_>` in future versions". The usual solution is to use [autoref-based specialization](https://lukaskalbertodt.github.io/2019/12/05/generalized-autoref-based-specialization.html) as [we do for `AsRef`](https://docs.rs/derive_more/2.0.1/src/derive_more/as.rs.html). However, autoref-based specialization doesn't work in a generic context (i.e. `MyError<E1, E2>`), since it requires trait bounds to work, and we cannot describe conditional trait bounds in Rust, which leads to the situation that this case could not be supported at all:
```rust
struct MyError<E> {
    source: Option<E>,
}
```

So, choosing between checking the field's type syntactically (and so loosing support for renamed types) and not supporting optional `source` in generic contexts, I would definitely choose to support generics despite having naming issues in edge cases. As the result, this PR implements the similar syntactically-based solution as the `thiserror` crate does. The implications of this are warned and described in the documentation.

## Future possibilities

Additionally, we may support `#[error(source(optional))]` attribute, which will allow using renamed types naturally:
```rust
use derive_more::{Display, Error};

#[derive(Debug, Display, Error)]
struct Simple;

#[derive(Debug, Display, Error)]
#[display("Oops!")]
struct Generic(#[error(source(optional))] RenamedOption<Simple>); // forces recognizing as `Option`
type RenamedOption<T> = Option<T>;
```
However, it's better to be implemented as a separate PR, to not overcomplicate this one.

## Checklist

- [x] Documentation is updated
- [x] Tests are added/updated
- [x] [CHANGELOG entry](/CHANGELOG.md) is added


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9b3e44f..cbd7572 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 - Support `#[display(rename_all = "<casing>")]` attribute to change output for
   implicit naming of unit enum variants or unit structs when deriving `Display`.
   ([#443](https://github.com/JelteF/derive_more/pull/443))
+- Support `Option` fields for `Error::source()` in `Error` derive.
+  ([#459](https://github.com/JelteF/derive_more/pull/459))
 
 ### Fixed
 
diff --git a/impl/doc/error.md b/impl/doc/error.md
index 2aace51..ad41c92 100644
--- a/impl/doc/error.md
+++ b/impl/doc/error.md
@@ -52,6 +52,38 @@ Backtraces don't work though, because the `Backtrace` type is only available in
 `std`.
 
 
+### `Option`al fields
+
+Deriving `source()` is supported naturally for `Option<_>`-typed fields.
+
+> **WARNING**: Field type is **considered as `Option<_>` only syntactically**,
+> meaning that:
+> - Neither renaming `std::option::Option` would work:
+>   ```rust,compile_fail
+>   # use derive_more::{Display, Error};
+>   #
+>   #[derive(Debug, Display, Error)]
+>   struct Simple;
+>
+>   #[derive(Debug, Display, Error)]
+>   #[display("Oops!")]
+>   struct Generic(RenamedOption<Simple>); // not recognized as `Option`
+>   type RenamedOption<T> = Option<T>;
+>   ```
+> - Nor using any custom type named as `Option` would:
+>   ```rust,compile_fail
+>   # use derive_more::{Display, Error};
+>   #
+>   # #[derive(Debug, Display, Error)]
+>   # struct Simple;
+>   #
+>   #[derive(Debug, Display, Error)]
+>   #[display("No way!")]
+>   struct Generic(Option<Simple>);        // cannot use `?` syntax
+>   struct Option<T>(T);
+>   ```
+
+
 
 
 ## Example usage
@@ -79,10 +111,21 @@ struct WithSource {
     source: Simple,
 }
 #[derive(Default, Debug, Display, Error)]
+#[display("WithOptionalSource")]
+struct WithOptionalSource {
+    source: Option<Simple>,
+}
+#[derive(Default, Debug, Display, Error)]
 struct WithExplicitSource {
     #[error(source)]
     explicit_source: Simple,
 }
+#[derive(Default, Debug, Display, Error)]
+#[display("WithExplicitOptionalSource")]
+struct WithExplicitOptionalSource {
+    #[error(source)]
+    explicit_source: Option<Simple>,
+}
 
 #[derive(Default, Debug, Display, Error)]
 struct Tuple(Simple);
@@ -104,6 +147,10 @@ enum CompoundError {
     WithSource {
         source: Simple,
     },
+    #[display("WithOptionalSource")]
+    WithOptionalSource {
+        source: Option<Simple>,
+    },
     #[from(ignore)]
     WithBacktraceFromSource {
         #[error(backtrace)]
@@ -118,21 +165,35 @@ enum CompoundError {
         #[error(source)]
         explicit_source: WithSource,
     },
+    #[display("WithExplicitOptionalSource")]
+    WithExplicitOptionalSource {
+        #[error(source)]
+        explicit_source: Option<WithSource>,
+    },
     #[from(ignore)]
     WithBacktraceFromExplicitSource {
         #[error(backtrace, source)]
         explicit_source: WithSource,
     },
     Tuple(WithExplicitSource),
+    #[display("TupleOptional")]
+    TupleOptional(Option<WithExplicitSource>),
     WithoutSource(#[error(not(source))] Tuple),
 }
 
 assert!(Simple.source().is_none());
 assert!(request_ref::<Backtrace>(&Simple).is_none());
+
 assert!(WithSource::default().source().is_some());
+assert!(WithOptionalSource { source: Some(Simple) }.source().is_some());
+
 assert!(WithExplicitSource::default().source().is_some());
+assert!(WithExplicitOptionalSource { explicit_source: Some(Simple) }.source().is_some());
+
 assert!(Tuple::default().source().is_some());
+
 assert!(WithoutSource::default().source().is_none());
+
 let with_source_and_backtrace = WithSourceAndBacktrace {
     source: Simple,
     backtrace: Backtrace::capture(),
@@ -141,9 +202,16 @@ assert!(with_source_and_backtrace.source().is_some());
 assert!(request_ref::<Backtrace>(&with_source_and_backtrace).is_some());
 
 assert!(CompoundError::Simple.source().is_none());
+
 assert!(CompoundError::from(Simple).source().is_some());
+assert!(CompoundError::from(Some(Simple)).source().is_some());
+
 assert!(CompoundError::from(WithSource::default()).source().is_some());
+assert!(CompoundError::from(Some(WithSource::default())).source().is_some());
+
 assert!(CompoundError::from(WithExplicitSource::default()).source().is_some());
+assert!(CompoundError::from(Some(WithExplicitSource::default())).source().is_some());
+
 assert!(CompoundError::from(Tuple::default()).source().is_none());
 # }
 ```
diff --git a/impl/src/error.rs b/impl/src/error.rs
index 76382d1..be6c105 100644
--- a/impl/src/error.rs
+++ b/impl/src/error.rs
@@ -43,7 +43,7 @@ pub fn expand(
             // TODO: Use `derive_more::core::error::Error` once `error_in_core` Rust feature is
             //       stabilized.
             fn source(&self) -> Option<&(dyn derive_more::with_trait::Error + 'static)> {
-                use derive_more::__private::AsDynError;
+                use derive_more::__private::AsDynError as _;
                 #source
             }
         }
@@ -207,13 +207,17 @@ impl ParsedFields<'_, '_> {
     fn render_source_as_struct(&self) -> Option<TokenStream> {
         let source = self.source?;
         let ident = &self.data.members[source];
-        Some(render_some(quote! { #ident }))
+        Some(render_some(
+            quote! { (&#ident) },
+            self.data.field_types[source].is_option(),
+        ))
     }
 
     fn render_source_as_enum_variant_match_arm(&self) -> Option<TokenStream> {
         let source = self.source?;
         let pattern = self.data.matcher(&[source], &[quote! { source }]);
-        let expr = render_some(quote! { source });
+        let expr =
+            render_some(quote! { source }, self.data.field_types[source].is_option());
         Some(quote! { #pattern => #expr })
     }
 
@@ -287,10 +291,10 @@ impl ParsedFields<'_, '_> {
     }
 }
 
-fn render_some<T>(expr: T) -> TokenStream
-where
-    T: quote::ToTokens,
-{
+fn render_some(mut expr: TokenStream, unpack: bool) -> TokenStream {
+    if unpack {
+        expr = quote! { derive_more::core::option::Option::as_ref(#expr)? }
+    }
     quote! { Some(#expr.as_dyn_error()) }
 }
 
@@ -500,6 +504,42 @@ fn add_bound_if_type_parameter_used_in_type(
     ty: &syn::Type,
 ) {
     if let Some(ty) = utils::get_if_type_parameter_used_in_type(type_params, ty) {
-        bounds.insert(ty);
+        bounds.insert(ty.get_option_inner().cloned().unwrap_or(ty));
+    }
+}
+
+/// Extension of a [`syn::Type`] used by this expansion.
+trait TypeExt {
+    /// Checks syntactically whether this [`syn::Type`] represents an [`Option`].
+    fn is_option(&self) -> bool;
+
+    /// Returns the inner [`syn::Type`] if this one represents an [`Option`].
+    fn get_option_inner(&self) -> Option<&Self>;
+}
+
+impl TypeExt for syn::Type {
+    fn is_option(&self) -> bool {
+        self.get_option_inner().is_some()
+    }
+
+    fn get_option_inner(&self) -> Option<&Self> {
+        match self {
+            Self::Group(g) => g.elem.get_option_inner(),
+            Self::Paren(p) => p.elem.get_option_inner(),
+            Self::Path(p) => p
+                .path
+                .segments
+                .last()
+                .filter(|s| s.ident == "Option")
+                .and_then(|s| {
+                    if let syn::PathArguments::AngleBracketed(a) = &s.arguments {
+                        if let Some(syn::GenericArgument::Type(ty)) = a.args.first() {
+                            return Some(ty);
+                        }
+                    }
+                    None
+                }),
+            _ => None,
+        }
     }
 }


Test patch:
diff --git a/tests/error/derives_for_enums_with_source.rs b/tests/error/derives_for_enums_with_source.rs
index e5c6c36..3de323a 100644
--- a/tests/error/derives_for_enums_with_source.rs
+++ b/tests/error/derives_for_enums_with_source.rs
@@ -13,11 +13,20 @@ enum TestErr {
         source: SimpleErr,
         field: i32,
     },
+    NamedImplicitOptionalSource {
+        source: Option<SimpleErr>,
+        field: i32,
+    },
     #[cfg(feature = "std")]
     NamedImplicitBoxedSource {
         source: Box<dyn Error + Send + 'static>,
         field: i32,
     },
+    #[cfg(feature = "std")]
+    NamedImplicitOptionalBoxedSource {
+        source: Option<Box<dyn Error + Send + 'static>>,
+        field: i32,
+    },
     NamedExplicitNoSource {
         #[error(not(source))]
         source: SimpleErr,
@@ -28,6 +37,11 @@ enum TestErr {
         explicit_source: SimpleErr,
         field: i32,
     },
+    NamedExplicitOptionalSource {
+        #[error(source)]
+        explicit_source: Option<SimpleErr>,
+        field: i32,
+    },
     NamedExplicitNoSourceRedundant {
         #[error(not(source))]
         field: i32,
@@ -37,20 +51,33 @@ enum TestErr {
         source: SimpleErr,
         field: i32,
     },
+    NamedExplicitOptionalSourceRedundant {
+        #[error(source)]
+        source: Option<SimpleErr>,
+        field: i32,
+    },
     NamedExplicitSuppressesImplicit {
         source: i32,
         #[error(source)]
         field: SimpleErr,
     },
+    NamedExplicitOptionalSuppressesImplicit {
+        source: i32,
+        #[error(source)]
+        field: Option<SimpleErr>,
+    },
     UnnamedImplicitNoSource(i32, i32),
     UnnamedImplicitSource(SimpleErr),
+    UnnamedImplicitOptionalSource(Option<SimpleErr>),
     UnnamedExplicitNoSource(#[error(not(source))] SimpleErr),
     UnnamedExplicitSource(#[error(source)] SimpleErr, i32),
+    UnnamedExplicitOptionalSource(#[error(source)] Option<SimpleErr>, i32),
     UnnamedExplicitNoSourceRedundant(
         #[error(not(source))] i32,
         #[error(not(source))] i32,
     ),
     UnnamedExplicitSourceRedundant(#[error(source)] SimpleErr),
+    UnnamedExplicitOptionalSourceRedundant(#[error(source)] Option<SimpleErr>),
     NamedIgnore {
         #[error(ignore)]
         source: SimpleErr,
@@ -100,6 +127,17 @@ fn named_implicit_source() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[test]
+fn named_implicit_optional_source() {
+    let err = TestErr::NamedImplicitOptionalSource {
+        source: Some(SimpleErr),
+        field: 0,
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
 #[cfg(feature = "std")]
 #[test]
 fn named_implicit_boxed_source() {
@@ -112,6 +150,18 @@ fn named_implicit_boxed_source() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[cfg(feature = "std")]
+#[test]
+fn named_implicit_optional_boxed_source() {
+    let err = TestErr::NamedImplicitOptionalBoxedSource {
+        source: Some(Box::new(SimpleErr)),
+        field: 0,
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
 #[test]
 fn named_explicit_no_source() {
     let err = TestErr::NamedExplicitNoSource {
@@ -133,6 +183,17 @@ fn named_explicit_source() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[test]
+fn named_explicit_optional_source() {
+    let err = TestErr::NamedExplicitOptionalSource {
+        explicit_source: Some(SimpleErr),
+        field: 0,
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
 #[test]
 fn named_explicit_no_source_redundant() {
     let err = TestErr::NamedExplicitNoSourceRedundant { field: 0 };
@@ -151,6 +212,17 @@ fn named_explicit_source_redundant() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[test]
+fn named_explicit_optional_source_redundant() {
+    let err = TestErr::NamedExplicitOptionalSourceRedundant {
+        source: Some(SimpleErr),
+        field: 0,
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
 #[test]
 fn named_explicit_suppresses_implicit() {
     let err = TestErr::NamedExplicitSuppressesImplicit {
@@ -162,6 +234,17 @@ fn named_explicit_suppresses_implicit() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[test]
+fn named_explicit_optional_suppresses_implicit() {
+    let err = TestErr::NamedExplicitOptionalSuppressesImplicit {
+        source: 0,
+        field: Some(SimpleErr),
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
 #[test]
 fn unnamed_implicit_no_source() {
     assert!(TestErr::UnnamedImplicitNoSource(0, 0).source().is_none());
@@ -175,6 +258,14 @@ fn unnamed_implicit_source() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[test]
+fn unnamed_implicit_optional_source() {
+    let err = TestErr::UnnamedImplicitOptionalSource(Some(SimpleErr));
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
 #[test]
 fn unnamed_explicit_no_source() {
     let err = TestErr::UnnamedExplicitNoSource(SimpleErr);
@@ -190,6 +281,14 @@ fn unnamed_explicit_source() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[test]
+fn unnamed_explicit_optional_source() {
+    let err = TestErr::UnnamedExplicitOptionalSource(Some(SimpleErr), 0);
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
 #[test]
 fn unnamed_explicit_no_source_redundant() {
     let err = TestErr::UnnamedExplicitNoSourceRedundant(0, 0);
@@ -205,6 +304,14 @@ fn unnamed_explicit_source_redundant() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[test]
+fn unnamed_explicit_optional_source_redundant() {
+    let err = TestErr::UnnamedExplicitOptionalSourceRedundant(Some(SimpleErr));
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
 #[test]
 fn named_ignore() {
     let err = TestErr::NamedIgnore {
diff --git a/tests/error/derives_for_generic_enums_with_source.rs b/tests/error/derives_for_generic_enums_with_source.rs
index 5067043..2b10638 100644
--- a/tests/error/derives_for_generic_enums_with_source.rs
+++ b/tests/error/derives_for_generic_enums_with_source.rs
@@ -13,6 +13,10 @@ enum TestErr<E, T> {
         source: E,
         field: T,
     },
+    NamedImplicitOptionalSource {
+        source: Option<E>,
+        field: T,
+    },
     NamedExplicitNoSource {
         #[error(not(source))]
         source: E,
@@ -23,6 +27,11 @@ enum TestErr<E, T> {
         explicit_source: E,
         field: T,
     },
+    NamedExplicitOptionalSource {
+        #[error(source)]
+        explicit_source: Option<E>,
+        field: T,
+    },
     NamedExplicitNoSourceRedundant {
         #[error(not(source))]
         field: T,
@@ -32,17 +41,30 @@ enum TestErr<E, T> {
         source: E,
         field: T,
     },
+    NamedExplicitOptionalSourceRedundant {
+        #[error(source)]
+        source: Option<E>,
+        field: T,
+    },
     NamedExplicitSuppressesImplicit {
         source: T,
         #[error(source)]
         field: E,
     },
+    NamedExplicitOptionalSuppressesImplicit {
+        source: T,
+        #[error(source)]
+        field: Option<E>,
+    },
     UnnamedImplicitNoSource(T, T),
     UnnamedImplicitSource(E),
+    UnnamedImplicitOptionalSource(Option<E>),
     UnnamedExplicitNoSource(#[error(not(source))] E),
     UnnamedExplicitSource(#[error(source)] E, T),
+    UnnamedExplicitOptionalSource(#[error(source)] Option<E>, T),
     UnnamedExplicitNoSourceRedundant(#[error(not(source))] T, #[error(not(source))] T),
     UnnamedExplicitSourceRedundant(#[error(source)] E),
+    UnnamedExplicitOptionalSourceRedundant(#[error(source)] Option<E>),
     NamedIgnore {
         #[error(ignore)]
         source: E,
@@ -92,6 +114,17 @@ fn named_implicit_source() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[test]
+fn named_implicit_optional_source() {
+    let err = TestErr::NamedImplicitOptionalSource {
+        source: Some(SimpleErr),
+        field: 0,
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
 #[test]
 fn named_explicit_no_source() {
     let err = TestErr::NamedExplicitNoSource {
@@ -113,6 +146,17 @@ fn named_explicit_source() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[test]
+fn named_explicit_optional_source() {
+    let err = TestErr::NamedExplicitOptionalSource {
+        explicit_source: Some(SimpleErr),
+        field: 0,
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
 #[test]
 fn named_explicit_no_source_redundant() {
     let err = TestErr::<SimpleErr, _>::NamedExplicitNoSourceRedundant { field: 0 };
@@ -131,6 +175,17 @@ fn named_explicit_source_redundant() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[test]
+fn named_explicit_optional_source_redundant() {
+    let err = TestErr::NamedExplicitOptionalSourceRedundant {
+        source: Some(SimpleErr),
+        field: 0,
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
 #[test]
 fn named_explicit_suppresses_implicit() {
     let err = TestErr::NamedExplicitSuppressesImplicit {
@@ -142,6 +197,17 @@ fn named_explicit_suppresses_implicit() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[test]
+fn named_explicit_optional_suppresses_implicit() {
+    let err = TestErr::NamedExplicitOptionalSuppressesImplicit {
+        source: 0,
+        field: Some(SimpleErr),
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
 #[test]
 fn unnamed_implicit_no_source() {
     let err = TestErr::<SimpleErr, _>::UnnamedImplicitNoSource(0, 0);
@@ -157,6 +223,14 @@ fn unnamed_implicit_source() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[test]
+fn unnamed_implicit_optional_source() {
+    let err = TestErr::<_, i32>::UnnamedImplicitOptionalSource(Some(SimpleErr));
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
 #[test]
 fn unnamed_explicit_no_source() {
     let err = TestErr::<_, i32>::UnnamedExplicitNoSource(SimpleErr);
@@ -172,6 +246,14 @@ fn unnamed_explicit_source() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[test]
+fn unnamed_explicit_optional_source() {
+    let err = TestErr::UnnamedExplicitOptionalSource(Some(SimpleErr), 0);
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
 #[test]
 fn unnamed_explicit_no_source_redundant() {
     let err = TestErr::<SimpleErr, _>::UnnamedExplicitNoSourceRedundant(0, 0);
@@ -180,8 +262,9 @@ fn unnamed_explicit_no_source_redundant() {
 }
 
 #[test]
-fn unnamed_explicit_source_redundant() {
-    let err = TestErr::<_, i32>::UnnamedExplicitSourceRedundant(SimpleErr);
+fn unnamed_explicit_optional_source_redundant() {
+    let err =
+        TestErr::<_, i32>::UnnamedExplicitOptionalSourceRedundant(Some(SimpleErr));
 
     assert!(err.source().is_some());
     assert!(err.source().unwrap().is::<SimpleErr>());
diff --git a/tests/error/derives_for_generic_structs_with_source.rs b/tests/error/derives_for_generic_structs_with_source.rs
index 6426636..e4b633c 100644
--- a/tests/error/derives_for_generic_structs_with_source.rs
+++ b/tests/error/derives_for_generic_structs_with_source.rs
@@ -23,6 +23,25 @@ fn named_implicit_source() {
     }
 
     let err = TestErr::<SimpleErr, i32>::default();
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_implicit_optional_source() {
+    derive_display!(TestErr, E, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<E, T> {
+        source: Option<E>,
+        field: T,
+    }
+
+    let err = TestErr::<_, i32> {
+        source: Some(SimpleErr),
+        ..TestErr::default()
+    };
+
     assert!(err.source().is_some());
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
@@ -38,6 +57,7 @@ fn named_explicit_no_source() {
     }
 
     let err = TestErr::<SimpleErr, i32>::default();
+
     assert!(err.source().is_none());
 }
 
@@ -52,6 +72,26 @@ fn named_explicit_source() {
     }
 
     let err = TestErr::<SimpleErr, i32>::default();
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_explicit_optional_source() {
+    derive_display!(TestErr, E, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<E, T> {
+        #[error(source)]
+        explicit_source: Option<E>,
+        field: T,
+    }
+
+    let err = TestErr::<_, i32> {
+        explicit_source: Some(SimpleErr),
+        ..TestErr::default()
+    };
+
     assert!(err.source().is_some());
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
@@ -79,6 +119,26 @@ fn named_explicit_source_redundant() {
     }
 
     let err = TestErr::<SimpleErr, i32>::default();
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_explicit_optional_source_redundant() {
+    derive_display!(TestErr, E, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<E, T> {
+        #[error(source)]
+        source: Option<E>,
+        field: T,
+    }
+
+    let err = TestErr::<_, i32> {
+        source: Some(SimpleErr),
+        ..TestErr::default()
+    };
+
     assert!(err.source().is_some());
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
@@ -94,6 +154,26 @@ fn named_explicit_suppresses_implicit() {
     }
 
     let err = TestErr::<i32, SimpleErr>::default();
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_explicit_optional_suppresses_implicit() {
+    derive_display!(TestErr, E, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<E, T> {
+        source: E,
+        #[error(source)]
+        field: Option<T>,
+    }
+
+    let err = TestErr::<i32, _> {
+        field: Some(SimpleErr),
+        ..TestErr::default()
+    };
+
     assert!(err.source().is_some());
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
@@ -114,6 +194,19 @@ fn unnamed_implicit_source() {
     struct TestErr<E>(E);
 
     let err = TestErr::<SimpleErr>::default();
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn unnamed_implicit_optional_source() {
+    derive_display!(TestErr, E);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<E>(Option<E>);
+
+    let err = TestErr(Some(SimpleErr));
+
     assert!(err.source().is_some());
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
@@ -134,6 +227,22 @@ fn unnamed_explicit_source() {
     struct TestErr<E, T>(#[error(source)] E, T);
 
     let err = TestErr::<SimpleErr, i32>::default();
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn unnamed_explicit_optional_source() {
+    derive_display!(TestErr, E, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<E, T>(#[error(source)] Option<E>, T);
+
+    let err = TestErr::<_, i32> {
+        0: Some(SimpleErr),
+        ..TestErr::default()
+    };
+
     assert!(err.source().is_some());
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
@@ -154,6 +263,19 @@ fn unnamed_explicit_source_redundant() {
     struct TestErr<E>(#[error(source)] E);
 
     let err = TestErr::<SimpleErr>::default();
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn unnamed_explicit_optional_source_redundant() {
+    derive_display!(TestErr, E);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<E>(#[error(source)] Option<E>);
+
+    let err = TestErr(Some(SimpleErr));
+
     assert!(err.source().is_some());
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
diff --git a/tests/error/derives_for_structs_with_source.rs b/tests/error/derives_for_structs_with_source.rs
index 2267c16..c14c0bc 100644
--- a/tests/error/derives_for_structs_with_source.rs
+++ b/tests/error/derives_for_structs_with_source.rs
@@ -28,6 +28,25 @@ fn named_implicit_source() {
     }
 
     let err = TestErr::default();
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_implicit_optional_source() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr {
+        source: Option<SimpleErr>,
+        field: i32,
+    }
+
+    let err = TestErr {
+        source: Some(SimpleErr),
+        ..TestErr::default()
+    };
+
     assert!(err.source().is_some());
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
@@ -56,6 +75,26 @@ fn named_explicit_source() {
     }
 
     let err = TestErr::default();
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_explicit_optional_source() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr {
+        #[error(source)]
+        explicit_source: Option<SimpleErr>,
+        field: i32,
+    }
+
+    let err = TestErr {
+        explicit_source: Some(SimpleErr),
+        ..TestErr::default()
+    };
+
     assert!(err.source().is_some());
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
@@ -83,6 +122,26 @@ fn named_explicit_source_redundant() {
     }
 
     let err = TestErr::default();
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_explicit_optional_source_redundant() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr {
+        #[error(source)]
+        source: Option<SimpleErr>,
+        field: i32,
+    }
+
+    let err = TestErr {
+        source: Some(SimpleErr),
+        ..TestErr::default()
+    };
+
     assert!(err.source().is_some());
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
@@ -98,6 +157,26 @@ fn named_explicit_suppresses_implicit() {
     }
 
     let err = TestErr::default();
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_explicit_optional_suppresses_implicit() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr {
+        source: i32,
+        #[error(source)]
+        field: Option<SimpleErr>,
+    }
+
+    let err = TestErr {
+        field: Some(SimpleErr),
+        ..TestErr::default()
+    };
+
     assert!(err.source().is_some());
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
@@ -118,6 +197,19 @@ fn unnamed_implicit_source() {
     struct TestErr(SimpleErr);
 
     let err = TestErr::default();
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn unnamed_implicit_optional_source() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr(Option<SimpleErr>);
+
+    let err = TestErr(Some(SimpleErr));
+
     assert!(err.source().is_some());
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
@@ -138,6 +230,22 @@ fn unnamed_explicit_source() {
     struct TestErr(#[error(source)] SimpleErr, i32);
 
     let err = TestErr::default();
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn unnamed_explicit_optional_source() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr(#[error(source)] Option<SimpleErr>, i32);
+
+    let err = TestErr {
+        0: Some(SimpleErr),
+        ..TestErr::default()
+    };
+
     assert!(err.source().is_some());
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
@@ -158,6 +266,19 @@ fn unnamed_explicit_source_redundant() {
     struct TestErr(#[error(source)] SimpleErr);
 
     let err = TestErr::default();
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn unnamed_explicit_optional_source_redundant() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr(#[error(source)] Option<SimpleErr>);
+
+    let err = TestErr(Some(SimpleErr));
+
     assert!(err.source().is_some());
     assert!(err.source().unwrap().is::<SimpleErr>());
 }