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:
Support `#[error(source(optional))]` attribute (#426)
Resolves #426    
Follows #459

## Synopsis

See #459:
> 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>;
> ```



## Solution

Adds support for `#[error(source(optional))]` attribute.

## Checklist

- [x] Documentation is updated
- [x] Tests are added/updated
- [x] ~~[CHANGELOG entry](/CHANGELOG.md) is added~~ (not required)


Patch:
diff --git a/impl/doc/error.md b/impl/doc/error.md
index ad41c92..aa44eb9 100644
--- a/impl/doc/error.md
+++ b/impl/doc/error.md
@@ -83,6 +83,20 @@ Deriving `source()` is supported naturally for `Option<_>`-typed fields.
 >   struct Option<T>(T);
 >   ```
 
+> **TIP**: If `std::option::Option` for some reason needs to be renamed,
+> annotate the field with the `#[error(source(optional))]` attribute:
+> ```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>);
+> type RenamedOption<T> = Option<T>;
+>  ```
+
 
 
 
@@ -90,17 +104,19 @@ Deriving `source()` is supported naturally for `Option<_>`-typed fields.
 
 ```rust
 # #![cfg_attr(nightly, feature(error_generic_member_access))]
-// Nightly requires enabling this feature:
-// #![feature(error_generic_member_access)]
+# // Nightly requires enabling this feature:
+# // #![feature(error_generic_member_access)]
 # #[cfg(not(nightly))] fn main() {}
 # #[cfg(nightly)] fn main() {
 # use core::error::{request_ref, request_value, Error as __};
 # use std::backtrace::Backtrace;
 #
 # use derive_more::{Display, Error, From};
-
-// std::error::Error requires std::fmt::Debug and std::fmt::Display,
-// so we can also use derive_more::Display for fully declarative
+#
+# type RenamedOption<T> = Option<T>;
+#
+// `std::error::Error` requires `std::fmt::Debug` and `std::fmt::Display`,
+// so we can also use `derive_more::Display` for fully declarative
 // error-type definitions.
 
 #[derive(Default, Debug, Display, Error)]
@@ -126,6 +142,12 @@ struct WithExplicitOptionalSource {
     #[error(source)]
     explicit_source: Option<Simple>,
 }
+#[derive(Default, Debug, Display, Error)]
+#[display("WithExplicitOptionalSource")]
+struct WithExplicitRenamedOptionalSource {
+    #[error(source(optional))]
+    explicit_source: RenamedOption<Simple>,
+}
 
 #[derive(Default, Debug, Display, Error)]
 struct Tuple(Simple);
@@ -170,6 +192,11 @@ enum CompoundError {
         #[error(source)]
         explicit_source: Option<WithSource>,
     },
+    #[display("WithExplicitRenamedOptionalSource")]
+    WithExplicitRenamedOptionalSource {
+        #[error(source(optional))]
+        explicit_source: RenamedOption<WithExplicitRenamedOptionalSource>,
+    },
     #[from(ignore)]
     WithBacktraceFromExplicitSource {
         #[error(backtrace, source)]
@@ -189,6 +216,7 @@ assert!(WithOptionalSource { source: Some(Simple) }.source().is_some());
 
 assert!(WithExplicitSource::default().source().is_some());
 assert!(WithExplicitOptionalSource { explicit_source: Some(Simple) }.source().is_some());
+assert!(WithExplicitRenamedOptionalSource { explicit_source: Some(Simple) }.source().is_some());
 
 assert!(Tuple::default().source().is_some());
 
@@ -211,6 +239,7 @@ 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(Some(WithExplicitRenamedOptionalSource { explicit_source: Some(Simple) })).source().is_some());
 
 assert!(CompoundError::from(Tuple::default()).source().is_none());
 # }
diff --git a/impl/src/error.rs b/impl/src/error.rs
index be6c105..30b1265 100644
--- a/impl/src/error.rs
+++ b/impl/src/error.rs
@@ -181,7 +181,7 @@ fn allowed_attr_params() -> AttrParams {
         enum_: vec!["ignore"],
         struct_: vec!["ignore"],
         variant: vec!["ignore"],
-        field: vec!["ignore", "source", "backtrace"],
+        field: vec!["ignore", "source", "optional", "backtrace"],
     }
 }
 
@@ -207,17 +207,19 @@ 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) },
-            self.data.field_types[source].is_option(),
-        ))
+        let is_optional = self.data.infos[source].info.source_optional == Some(true)
+            || self.data.field_types[source].is_option();
+
+        Some(render_some(quote! { (&#ident) }, is_optional))
     }
 
     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 }, self.data.field_types[source].is_option());
+        let is_optional = self.data.infos[source].info.source_optional == Some(true)
+            || self.data.field_types[source].is_option();
+
+        let expr = render_some(quote! { source }, is_optional);
         Some(quote! { #pattern => #expr })
     }
 
@@ -344,10 +346,15 @@ fn parse_fields<'input, 'state>(
     }?;
 
     if let Some(source) = parsed_fields.source {
+        let is_optional = parsed_fields.data.infos[source].info.source_optional
+            == Some(true)
+            || state.fields[source].ty.is_option();
+
         add_bound_if_type_parameter_used_in_type(
             &mut parsed_fields.bounds,
             type_params,
             &state.fields[source].ty,
+            is_optional,
         );
     }
 
@@ -502,9 +509,16 @@ fn add_bound_if_type_parameter_used_in_type(
     bounds: &mut HashSet<syn::Type>,
     type_params: &HashSet<syn::Ident>,
     ty: &syn::Type,
+    unpack: bool,
 ) {
     if let Some(ty) = utils::get_if_type_parameter_used_in_type(type_params, ty) {
-        bounds.insert(ty.get_option_inner().cloned().unwrap_or(ty));
+        bounds.insert(
+            unpack
+                .then(|| ty.get_inner())
+                .flatten()
+                .cloned()
+                .unwrap_or(ty),
+        );
     }
 }
 
@@ -513,8 +527,20 @@ 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 a wrapper.
+    ///
+    /// `filter` filters out this [`syn::Type`] by its name.
+    fn get_inner_if(&self, filter: impl Fn(&syn::Ident) -> bool) -> Option<&Self>;
+
+    /// Returns the inner [`syn::Type`] if this one represents a wrapper.
+    fn get_inner(&self) -> Option<&Self> {
+        self.get_inner_if(|_| true)
+    }
+
     /// Returns the inner [`syn::Type`] if this one represents an [`Option`].
-    fn get_option_inner(&self) -> Option<&Self>;
+    fn get_option_inner(&self) -> Option<&Self> {
+        self.get_inner_if(|ident| ident == "Option")
+    }
 }
 
 impl TypeExt for syn::Type {
@@ -522,7 +548,7 @@ impl TypeExt for syn::Type {
         self.get_option_inner().is_some()
     }
 
-    fn get_option_inner(&self) -> Option<&Self> {
+    fn get_inner_if(&self, filter: impl Fn(&syn::Ident) -> bool) -> Option<&Self> {
         match self {
             Self::Group(g) => g.elem.get_option_inner(),
             Self::Paren(p) => p.elem.get_option_inner(),
@@ -530,7 +556,7 @@ impl TypeExt for syn::Type {
                 .path
                 .segments
                 .last()
-                .filter(|s| s.ident == "Option")
+                .filter(|s| filter(&s.ident))
                 .and_then(|s| {
                     if let syn::PathArguments::AngleBracketed(a) = &s.arguments {
                         if let Some(syn::GenericArgument::Type(ty)) = a.args.first() {
diff --git a/impl/src/utils.rs b/impl/src/utils.rs
index 85d815b..07e7e1a 100644
--- a/impl/src/utils.rs
+++ b/impl/src/utils.rs
@@ -921,6 +921,8 @@ fn parse_punctuated_nested_meta(
                     (None, "ref") => info.ref_ = Some(true),
                     (None, "ref_mut") => info.ref_mut = Some(true),
 
+                    (None, "source") => info.source = Some(true),
+
                     #[cfg(any(feature = "from", feature = "into"))]
                     (None, "types")
                     | (Some("owned"), "types")
@@ -1022,6 +1024,7 @@ fn parse_punctuated_nested_meta(
                     (None, "ref_mut") => info.ref_mut = Some(true),
                     (None, "source") => info.source = Some(true),
                     (Some("not"), "source") => info.source = Some(false),
+                    (Some("source"), "optional") => info.source_optional = Some(true),
                     (None, "backtrace") => info.backtrace = Some(true),
                     (Some("not"), "backtrace") => info.backtrace = Some(false),
                     _ => {
@@ -1218,6 +1221,7 @@ pub struct MetaInfo {
     pub ref_: Option<bool>,
     pub ref_mut: Option<bool>,
     pub source: Option<bool>,
+    pub source_optional: Option<bool>,
     pub backtrace: Option<bool>,
     #[cfg(any(feature = "from", feature = "into"))]
     pub types: HashMap<RefType, HashSet<syn::Type>>,


Test patch:
diff --git a/tests/error/derives_for_enums_with_source.rs b/tests/error/derives_for_enums_with_source.rs
index 3de323a..b546c15 100644
--- a/tests/error/derives_for_enums_with_source.rs
+++ b/tests/error/derives_for_enums_with_source.rs
@@ -2,6 +2,8 @@
 
 use super::*;
 
+type RenamedOption<T> = Option<T>;
+
 derive_display!(TestErr);
 #[derive(Debug, Error)]
 enum TestErr {
@@ -13,15 +15,15 @@ enum TestErr {
         source: SimpleErr,
         field: i32,
     },
-    NamedImplicitOptionalSource {
-        source: Option<SimpleErr>,
-        field: i32,
-    },
     #[cfg(feature = "std")]
     NamedImplicitBoxedSource {
         source: Box<dyn Error + Send + 'static>,
         field: i32,
     },
+    NamedImplicitOptionalSource {
+        source: Option<SimpleErr>,
+        field: i32,
+    },
     #[cfg(feature = "std")]
     NamedImplicitOptionalBoxedSource {
         source: Option<Box<dyn Error + Send + 'static>>,
@@ -42,6 +44,17 @@ enum TestErr {
         explicit_source: Option<SimpleErr>,
         field: i32,
     },
+    NamedExplicitRenamedOptionalSource {
+        #[error(source(optional))]
+        explicit_source: RenamedOption<SimpleErr>,
+        field: i32,
+    },
+    #[cfg(feature = "std")]
+    NamedExplicitRenamedOptionalBoxedSource {
+        #[error(source(optional))]
+        explicit_source: RenamedOption<Box<dyn Error + Send + 'static>>,
+        field: i32,
+    },
     NamedExplicitNoSourceRedundant {
         #[error(not(source))]
         field: i32,
@@ -51,11 +64,6 @@ enum TestErr {
         source: SimpleErr,
         field: i32,
     },
-    NamedExplicitOptionalSourceRedundant {
-        #[error(source)]
-        source: Option<SimpleErr>,
-        field: i32,
-    },
     NamedExplicitSuppressesImplicit {
         source: i32,
         #[error(source)]
@@ -72,12 +80,20 @@ enum TestErr {
     UnnamedExplicitNoSource(#[error(not(source))] SimpleErr),
     UnnamedExplicitSource(#[error(source)] SimpleErr, i32),
     UnnamedExplicitOptionalSource(#[error(source)] Option<SimpleErr>, i32),
+    UnnamedExplicitRenamedOptionalSource(
+        #[error(source(optional))] RenamedOption<SimpleErr>,
+        i32,
+    ),
+    #[cfg(feature = "std")]
+    UnnamedExplicitRenamedOptionalBoxedSource(
+        #[error(source(optional))] RenamedOption<Box<dyn Error + Send + 'static>>,
+        i32,
+    ),
     UnnamedExplicitNoSourceRedundant(
         #[error(not(source))] i32,
         #[error(not(source))] i32,
     ),
     UnnamedExplicitSourceRedundant(#[error(source)] SimpleErr),
-    UnnamedExplicitOptionalSourceRedundant(#[error(source)] Option<SimpleErr>),
     NamedIgnore {
         #[error(ignore)]
         source: SimpleErr,
@@ -195,16 +211,21 @@ fn named_explicit_optional_source() {
 }
 
 #[test]
-fn named_explicit_no_source_redundant() {
-    let err = TestErr::NamedExplicitNoSourceRedundant { field: 0 };
+fn named_explicit_renamed_optional_source() {
+    let err = TestErr::NamedExplicitRenamedOptionalSource {
+        explicit_source: Some(SimpleErr),
+        field: 0,
+    };
 
-    assert!(err.source().is_none());
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[cfg(feature = "std")]
 #[test]
-fn named_explicit_source_redundant() {
-    let err = TestErr::NamedExplicitSourceRedundant {
-        source: SimpleErr,
+fn named_explicit_renamed_optional_boxed_source() {
+    let err = TestErr::NamedExplicitRenamedOptionalBoxedSource {
+        explicit_source: Some(Box::new(SimpleErr)),
         field: 0,
     };
 
@@ -213,9 +234,16 @@ fn named_explicit_source_redundant() {
 }
 
 #[test]
-fn named_explicit_optional_source_redundant() {
-    let err = TestErr::NamedExplicitOptionalSourceRedundant {
-        source: Some(SimpleErr),
+fn named_explicit_no_source_redundant() {
+    let err = TestErr::NamedExplicitNoSourceRedundant { field: 0 };
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn named_explicit_source_redundant() {
+    let err = TestErr::NamedExplicitSourceRedundant {
+        source: SimpleErr,
         field: 0,
     };
 
@@ -290,23 +318,35 @@ fn unnamed_explicit_optional_source() {
 }
 
 #[test]
-fn unnamed_explicit_no_source_redundant() {
-    let err = TestErr::UnnamedExplicitNoSourceRedundant(0, 0);
+fn unnamed_explicit_renamed_optional_source() {
+    let err = TestErr::UnnamedExplicitRenamedOptionalSource(Some(SimpleErr), 0);
 
-    assert!(err.source().is_none());
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[cfg(feature = "std")]
 #[test]
-fn unnamed_explicit_source_redundant() {
-    let err = TestErr::UnnamedExplicitSourceRedundant(SimpleErr);
+fn unnamed_explicit_renamed_optional_boxed_source() {
+    let err = TestErr::UnnamedExplicitRenamedOptionalBoxedSource(
+        Some(Box::new(SimpleErr)),
+        0,
+    );
 
     assert!(err.source().is_some());
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
 #[test]
-fn unnamed_explicit_optional_source_redundant() {
-    let err = TestErr::UnnamedExplicitOptionalSourceRedundant(Some(SimpleErr));
+fn unnamed_explicit_no_source_redundant() {
+    let err = TestErr::UnnamedExplicitNoSourceRedundant(0, 0);
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn unnamed_explicit_source_redundant() {
+    let err = TestErr::UnnamedExplicitSourceRedundant(SimpleErr);
 
     assert!(err.source().is_some());
     assert!(err.source().unwrap().is::<SimpleErr>());
diff --git a/tests/error/derives_for_generic_enums_with_source.rs b/tests/error/derives_for_generic_enums_with_source.rs
index 2b10638..acf5a73 100644
--- a/tests/error/derives_for_generic_enums_with_source.rs
+++ b/tests/error/derives_for_generic_enums_with_source.rs
@@ -2,6 +2,8 @@
 
 use super::*;
 
+type RenamedOption<T> = Option<T>;
+
 derive_display!(TestErr, T, E);
 #[derive(Debug, Error)]
 enum TestErr<E, T> {
@@ -32,6 +34,11 @@ enum TestErr<E, T> {
         explicit_source: Option<E>,
         field: T,
     },
+    NamedExplicitRenamedOptionalSource {
+        #[error(source(optional))]
+        explicit_source: RenamedOption<E>,
+        field: T,
+    },
     NamedExplicitNoSourceRedundant {
         #[error(not(source))]
         field: T,
@@ -41,11 +48,6 @@ enum TestErr<E, T> {
         source: E,
         field: T,
     },
-    NamedExplicitOptionalSourceRedundant {
-        #[error(source)]
-        source: Option<E>,
-        field: T,
-    },
     NamedExplicitSuppressesImplicit {
         source: T,
         #[error(source)]
@@ -62,9 +64,12 @@ enum TestErr<E, T> {
     UnnamedExplicitNoSource(#[error(not(source))] E),
     UnnamedExplicitSource(#[error(source)] E, T),
     UnnamedExplicitOptionalSource(#[error(source)] Option<E>, T),
+    UnnamedExplicitRenamedOptionalSource(
+        #[error(source(optional))] RenamedOption<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,
@@ -157,6 +162,17 @@ fn named_explicit_optional_source() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[test]
+fn named_explicit_renamed_optional_source() {
+    let err = TestErr::NamedExplicitRenamedOptionalSource {
+        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 };
@@ -175,17 +191,6 @@ 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 {
@@ -255,19 +260,18 @@ fn unnamed_explicit_optional_source() {
 }
 
 #[test]
-fn unnamed_explicit_no_source_redundant() {
-    let err = TestErr::<SimpleErr, _>::UnnamedExplicitNoSourceRedundant(0, 0);
+fn unnamed_explicit_renamed_optional_source() {
+    let err = TestErr::UnnamedExplicitRenamedOptionalSource(Some(SimpleErr), 0);
 
-    assert!(err.source().is_none());
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
 #[test]
-fn unnamed_explicit_optional_source_redundant() {
-    let err =
-        TestErr::<_, i32>::UnnamedExplicitOptionalSourceRedundant(Some(SimpleErr));
+fn unnamed_explicit_no_source_redundant() {
+    let err = TestErr::<SimpleErr, _>::UnnamedExplicitNoSourceRedundant(0, 0);
 
-    assert!(err.source().is_some());
-    assert!(err.source().unwrap().is::<SimpleErr>());
+    assert!(err.source().is_none());
 }
 
 #[test]
diff --git a/tests/error/derives_for_generic_structs_with_source.rs b/tests/error/derives_for_generic_structs_with_source.rs
index e4b633c..9ac5ca6 100644
--- a/tests/error/derives_for_generic_structs_with_source.rs
+++ b/tests/error/derives_for_generic_structs_with_source.rs
@@ -2,6 +2,8 @@
 
 use super::*;
 
+type RenamedOption<T> = Option<T>;
+
 #[test]
 fn named_implicit_no_source() {
     derive_display!(TestErr, T);
@@ -96,6 +98,25 @@ fn named_explicit_optional_source() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[test]
+fn named_explicit_renamed_optional_source() {
+    derive_display!(TestErr, E, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<E, T> {
+        #[error(source(optional))]
+        explicit_source: RenamedOption<E>,
+        field: T,
+    }
+
+    let err = TestErr::<_, i32> {
+        explicit_source: Some(SimpleErr),
+        ..TestErr::default()
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
 #[test]
 fn named_explicit_no_source_redundant() {
     derive_display!(TestErr, T);
@@ -124,25 +145,6 @@ fn named_explicit_source_redundant() {
     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>());
-}
-
 #[test]
 fn named_explicit_suppresses_implicit() {
     derive_display!(TestErr, E, T);
@@ -247,6 +249,21 @@ fn unnamed_explicit_optional_source() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[test]
+fn unnamed_explicit_renamed_optional_source() {
+    derive_display!(TestErr, E, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<E, T>(#[error(source(optional))] RenamedOption<E>, T);
+
+    let err = TestErr::<_, i32> {
+        0: Some(SimpleErr),
+        ..TestErr::default()
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
 #[test]
 fn unnamed_explicit_no_source_redundant() {
     derive_display!(TestErr, T);
@@ -268,18 +285,6 @@ fn unnamed_explicit_source_redundant() {
     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>());
-}
-
 #[test]
 fn named_ignore() {
     derive_display!(TestErr, E, T);
diff --git a/tests/error/derives_for_structs_with_source.rs b/tests/error/derives_for_structs_with_source.rs
index c14c0bc..e53fde1 100644
--- a/tests/error/derives_for_structs_with_source.rs
+++ b/tests/error/derives_for_structs_with_source.rs
@@ -2,6 +2,8 @@
 
 use super::*;
 
+type RenamedOption<T> = Option<T>;
+
 #[test]
 fn unit() {
     assert!(SimpleErr.source().is_none());
@@ -33,6 +35,25 @@ fn named_implicit_source() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[cfg(feature = "std")]
+#[test]
+fn named_implicit_boxed_source() {
+    derive_display!(TestErr);
+    #[derive(Debug, Error)]
+    struct TestErr {
+        source: Box<dyn Error + Send + 'static>,
+        field: i32,
+    }
+
+    let err = TestErr {
+        source: Box::new(SimpleErr),
+        field: 0,
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
 #[test]
 fn named_implicit_optional_source() {
     derive_display!(TestErr);
@@ -51,6 +72,25 @@ fn named_implicit_optional_source() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[cfg(feature = "std")]
+#[test]
+fn named_implicit_optional_boxed_source() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr {
+        source: Option<Box<dyn Error + Send + 'static>>,
+        field: i32,
+    }
+
+    let err = TestErr {
+        source: Some(Box::new(SimpleErr)),
+        ..TestErr::default()
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
 #[test]
 fn named_explicit_no_source() {
     derive_display!(TestErr);
@@ -80,6 +120,26 @@ fn named_explicit_source() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[cfg(feature = "std")]
+#[test]
+fn named_explicit_boxed_source() {
+    derive_display!(TestErr);
+    #[derive(Debug, Error)]
+    struct TestErr {
+        #[error(source)]
+        explicit_source: Box<dyn Error + Send + 'static>,
+        field: i32,
+    }
+
+    let err = TestErr {
+        explicit_source: Box::new(SimpleErr),
+        field: 0,
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
 #[test]
 fn named_explicit_optional_source() {
     derive_display!(TestErr);
@@ -99,46 +159,58 @@ fn named_explicit_optional_source() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[cfg(feature = "std")]
 #[test]
-fn named_explicit_no_source_redundant() {
+fn named_explicit_optional_boxed_source() {
     derive_display!(TestErr);
     #[derive(Default, Debug, Error)]
     struct TestErr {
-        #[error(not(source))]
+        #[error(source)]
+        explicit_source: Option<Box<dyn Error + Send + 'static>>,
         field: i32,
     }
 
-    assert!(TestErr::default().source().is_none());
+    let err = TestErr {
+        explicit_source: Some(Box::new(SimpleErr)),
+        ..TestErr::default()
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
 #[test]
-fn named_explicit_source_redundant() {
+fn named_explicit_renamed_optional_source() {
     derive_display!(TestErr);
     #[derive(Default, Debug, Error)]
     struct TestErr {
-        #[error(source)]
-        source: SimpleErr,
+        #[error(source(optional))]
+        explicit_source: RenamedOption<SimpleErr>,
         field: i32,
     }
 
-    let err = TestErr::default();
+    let err = TestErr {
+        explicit_source: Some(SimpleErr),
+        ..TestErr::default()
+    };
 
     assert!(err.source().is_some());
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[cfg(feature = "std")]
 #[test]
-fn named_explicit_optional_source_redundant() {
+fn named_explicit_renamed_optional_boxed_source() {
     derive_display!(TestErr);
     #[derive(Default, Debug, Error)]
     struct TestErr {
-        #[error(source)]
-        source: Option<SimpleErr>,
+        #[error(source(optional))]
+        explicit_source: RenamedOption<Box<dyn Error + Send + 'static>>,
         field: i32,
     }
 
     let err = TestErr {
-        source: Some(SimpleErr),
+        explicit_source: Some(Box::new(SimpleErr)),
         ..TestErr::default()
     };
 
@@ -146,6 +218,34 @@ fn named_explicit_optional_source_redundant() {
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[test]
+fn named_explicit_no_source_redundant() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr {
+        #[error(not(source))]
+        field: i32,
+    }
+
+    assert!(TestErr::default().source().is_none());
+}
+
+#[test]
+fn named_explicit_source_redundant() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr {
+        #[error(source)]
+        source: SimpleErr,
+        field: i32,
+    }
+
+    let err = TestErr::default();
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
 #[test]
 fn named_explicit_suppresses_implicit() {
     derive_display!(TestErr);
@@ -251,33 +351,55 @@ fn unnamed_explicit_optional_source() {
 }
 
 #[test]
-fn unnamed_explicit_no_source_redundant() {
+fn unnamed_explicit_renamed_optional_source() {
     derive_display!(TestErr);
     #[derive(Default, Debug, Error)]
-    struct TestErr(#[error(not(source))] i32, #[error(not(source))] i32);
+    struct TestErr(#[error(source(optional))] RenamedOption<SimpleErr>, i32);
 
-    assert!(TestErr::default().source().is_none());
+    let err = TestErr {
+        0: Some(SimpleErr),
+        ..TestErr::default()
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
+#[cfg(feature = "std")]
 #[test]
-fn unnamed_explicit_source_redundant() {
+fn unnamed_explicit_renamed_optional_boxed_source() {
     derive_display!(TestErr);
     #[derive(Default, Debug, Error)]
-    struct TestErr(#[error(source)] SimpleErr);
+    struct TestErr(
+        #[error(source(optional))] RenamedOption<Box<dyn Error + Send + 'static>>,
+        i32,
+    );
 
-    let err = TestErr::default();
+    let err = TestErr {
+        0: Some(Box::new(SimpleErr)),
+        ..TestErr::default()
+    };
 
     assert!(err.source().is_some());
     assert!(err.source().unwrap().is::<SimpleErr>());
 }
 
 #[test]
-fn unnamed_explicit_optional_source_redundant() {
+fn unnamed_explicit_no_source_redundant() {
     derive_display!(TestErr);
     #[derive(Default, Debug, Error)]
-    struct TestErr(#[error(source)] Option<SimpleErr>);
+    struct TestErr(#[error(not(source))] i32, #[error(not(source))] i32);
 
-    let err = TestErr(Some(SimpleErr));
+    assert!(TestErr::default().source().is_none());
+}
+
+#[test]
+fn unnamed_explicit_source_redundant() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr(#[error(source)] SimpleErr);
+
+    let err = TestErr::default();
 
     assert!(err.source().is_some());
     assert!(err.source().unwrap().is::<SimpleErr>());