Repository: zkat/miette
Pull request #169 (2022-05-06 06:15:48)

Allow using smartpointers with source_diagnostic
Currently only a simple borrow is performed in some cases, and as_ref in other. AsRef is however not always implemented and `&Box<dyn Diagnostic>` does not implement `Diagnostic`. This patch makes use of `Borrow` that correctly handles both cases.


Problems before:
```
error[E0599]: the method `as_ref` exists for reference `&AnErr`, but its trait bounds were not satisfied
   --> tests/test_diagnostic_source_macro.rs:14:17
    |
5   | struct AnErr;
    | ------------- doesn't satisfy `AnErr: AsRef<_>`
...
14  | #[derive(Debug, miette::Diagnostic, thiserror::Error)]
    |                 ^^^^^^^^^^^^^^^^^^ method cannot be called on `&AnErr` due to unsatisfied trait bounds
    |
    = note: the following trait bounds were not satisfied:
            `AnErr: AsRef<_>`
            which is required by `&AnErr: AsRef<_>`
note: the following trait must be implemented
    = help: items from traits can only be used if the trait is implemented and in scope
    = note: the following trait defines an item `as_ref`, perhaps you need to implement it:
            candidate #1: `AsRef`
    = note: this error originates in the derive macro `miette::Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `Box<(dyn Diagnostic + 'static)>: Diagnostic` is not satisfied
  --> tests/test_diagnostic_source_macro.rs:29:17
   |
29 | #[derive(Debug, miette::Diagnostic, thiserror::Error)]
   |                 ^^^^^^^^^^^^^^^^^^ the trait `Diagnostic` is not implemented for `Box<(dyn Diagnostic + 'static)>`
   |
   = note: required for the cast to the object type `dyn Diagnostic`
   = note: this error originates in the derive macro `miette::Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `Arc<(dyn Diagnostic + 'static)>: Diagnostic` is not satisfied
  --> tests/test_diagnostic_source_macro.rs:33:17
   |
33 | #[derive(Debug, miette::Diagnostic, thiserror::Error)]
   |                 ^^^^^^^^^^^^^^^^^^ the trait `Diagnostic` is not implemented for `Arc<(dyn Diagnostic + 'static)>`
   |
   = note: required for the cast to the object type `dyn Diagnostic`
   = note: this error originates in the derive macro `miette::Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
```


Patch:
diff --git a/miette-derive/src/diagnostic_source.rs b/miette-derive/src/diagnostic_source.rs
index 949defe..46a8a64 100644
--- a/miette-derive/src/diagnostic_source.rs
+++ b/miette-derive/src/diagnostic_source.rs
@@ -59,7 +59,7 @@ impl DiagnosticSource {
                     };
                     quote! {
                         Self::#ident #display_pat => {
-                            std::option::Option::Some(#rel.as_ref())
+                            std::option::Option::Some(std::borrow::Borrow::borrow(#rel))
                         }
                     }
                 })
@@ -71,7 +71,7 @@ impl DiagnosticSource {
         let rel = &self.0;
         Some(quote! {
             fn diagnostic_source<'a>(&'a self) -> std::option::Option<&'a dyn miette::Diagnostic> {
-                std::option::Option::Some(&self.#rel)
+                std::option::Option::Some(std::borrow::Borrow::borrow(&self.#rel))
             }
         })
     }


Test patch:
diff --git a/tests/test_diagnostic_source_macro.rs b/tests/test_diagnostic_source_macro.rs
index d7c5d2d..8af2e88 100644
--- a/tests/test_diagnostic_source_macro.rs
+++ b/tests/test_diagnostic_source_macro.rs
@@ -6,15 +6,56 @@ struct AnErr;
 
 #[derive(Debug, miette::Diagnostic, thiserror::Error)]
 #[error("TestError")]
-struct TestError {
+struct TestStructError {
     #[diagnostic_source]
     asdf_inner_foo: AnErr,
 }
 
+#[derive(Debug, miette::Diagnostic, thiserror::Error)]
+#[error("TestError")]
+enum TestEnumError {
+    Without,
+    WithTuple(#[diagnostic_source] AnErr),
+    WithStruct {
+        #[diagnostic_source]
+        inner: AnErr,
+    },
+}
+
+#[derive(Debug, miette::Diagnostic, thiserror::Error)]
+#[error("TestError")]
+struct TestTupleError(#[diagnostic_source] AnErr);
+
+#[derive(Debug, miette::Diagnostic, thiserror::Error)]
+#[error("TestError")]
+struct TestBoxedError(#[diagnostic_source] Box<dyn Diagnostic>);
+
+#[derive(Debug, miette::Diagnostic, thiserror::Error)]
+#[error("TestError")]
+struct TestArcedError(#[diagnostic_source] std::sync::Arc<dyn Diagnostic>);
+
 #[test]
 fn test_diagnostic_source() {
-    let error = TestError {
+    let error = TestStructError {
         asdf_inner_foo: AnErr,
     };
     assert!(error.diagnostic_source().is_some());
+
+    let error = TestEnumError::Without;
+    assert!(error.diagnostic_source().is_none());
+
+    let error = TestEnumError::WithTuple(AnErr);
+    assert!(error.diagnostic_source().is_some());
+
+    let error = TestEnumError::WithStruct { inner: AnErr };
+    assert!(error.diagnostic_source().is_some());
+
+    let error = TestTupleError(AnErr);
+    assert!(error.diagnostic_source().is_some());
+
+    let error = TestBoxedError(Box::new(AnErr));
+    assert!(error.diagnostic_source().is_some());
+
+    let error = TestArcedError(std::sync::Arc::new(AnErr));
+    assert!(error.diagnostic_source().is_some());
 }