Repository: zkat/miette (rust)

Issue:
Related reports render with the severity of the parent report.
I would expect the related reports to have prefixed "Error: ", "Warning: " or "Advice: " according to the severity level they have, but instead it is printed with the severity level of the parent report.

Is this the intended behaviour or is it a bug?

---
With this code
```rust
#[derive(Diagnostic, Debug, Error)]
#[error("multiple errors")]
#[diagnostic(severity(error))]
pub struct MutiError {
    #[related]
    related: Vec<InnerError>,
}

#[derive(Diagnostic, Debug, Error)]
pub enum InnerError {
    #[error("a warning")]
    #[diagnostic(severity(warning))]
    Warning,
    #[error("an error")]
    #[diagnostic(severity(error))]
    Error,
}

fn main() -> miette::Result<()> {
    Err(MutiError {
        related: vec![InnerError::Warning, InnerError::Error],
    })?
}
```

I get this output:
![image](https://user-images.githubusercontent.com/38019254/213898744-8667f5ed-e669-4f0c-b5f4-0655a0ce9856.png)

But I would expect this:
![image](https://user-images.githubusercontent.com/38019254/213898754-337d6cdb-a3a3-483a-9e1c-980cbc0476f1.png)

---

I think is just these lines in the graphical and narratable reporters, where `diagnostic` is used instead of `rel`.

https://github.com/zkat/miette/blob/14f952dc91b9f27b7ae1e9e03b89744bcc682fa2/src/handlers/graphical.rs#L283-L288
https://github.com/zkat/miette/blob/14f952dc91b9f27b7ae1e9e03b89744bcc682fa2/src/handlers/narratable.rs#L134-L139

Pull request description:
Fix wrong severity of related errors
Currently, the severity of the parent error is shown as the severity of related errors.
This PR fix it.

Closes #232

Patch:
diff --git a/clippy.toml b/clippy.toml
new file mode 100644
index 0000000..0d369b5
--- /dev/null
+++ b/clippy.toml
@@ -0,0 +1,1 @@
+msrv = "1.56.0"
diff --git a/src/handlers/graphical.rs b/src/handlers/graphical.rs
index 15d2980..7c8685d 100644
--- a/src/handlers/graphical.rs
+++ b/src/handlers/graphical.rs
@@ -281,7 +281,7 @@ impl GraphicalReportHandler {
         if let Some(related) = diagnostic.related() {
             writeln!(f)?;
             for rel in related {
-                match diagnostic.severity() {
+                match rel.severity() {
                     Some(Severity::Error) | None => write!(f, "Error: ")?,
                     Some(Severity::Warning) => write!(f, "Warning: ")?,
                     Some(Severity::Advice) => write!(f, "Advice: ")?,
diff --git a/src/handlers/narratable.rs b/src/handlers/narratable.rs
index 5855752..d90298a 100644
--- a/src/handlers/narratable.rs
+++ b/src/handlers/narratable.rs
@@ -132,7 +132,7 @@ impl NarratableReportHandler {
         if let Some(related) = diagnostic.related() {
             writeln!(f)?;
             for rel in related {
-                match diagnostic.severity() {
+                match rel.severity() {
                     Some(Severity::Error) | None => write!(f, "Error: ")?,
                     Some(Severity::Warning) => write!(f, "Warning: ")?,
                     Some(Severity::Advice) => write!(f, "Advice: ")?,
@@ -218,10 +218,10 @@ impl NarratableReportHandler {
         Ok(())
     }
 
-    fn render_context<'a>(
+    fn render_context(
         &self,
         f: &mut impl fmt::Write,
-        source: &'a dyn SourceCode,
+        source: &dyn SourceCode,
         context: &LabeledSpan,
         labels: &[LabeledSpan],
     ) -> fmt::Result {
diff --git a/src/protocol.rs b/src/protocol.rs
index d94011a..5b657b4 100644
--- a/src/protocol.rs
+++ b/src/protocol.rs
@@ -488,7 +488,7 @@ impl SourceOffset {
         Ok((
             loc.file().into(),
             fs::read_to_string(loc.file())
-                .map(|txt| Self::from_location(&txt, loc.line() as usize, loc.column() as usize))?,
+                .map(|txt| Self::from_location(txt, loc.line() as usize, loc.column() as usize))?,
         ))
     }
 }


Test patch:
diff --git a/tests/graphical.rs b/tests/graphical.rs
index 69f5bff..34dc433 100644
--- a/tests/graphical.rs
+++ b/tests/graphical.rs
@@ -1057,6 +1057,132 @@ Error: oops::my::bad
     Ok(())
 }
 
+#[test]
+fn related_severity() -> Result<(), MietteError> {
+    #[derive(Debug, Diagnostic, Error)]
+    #[error("oops!")]
+    #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
+    struct MyBad {
+        #[source_code]
+        src: NamedSource,
+        #[label("this bit here")]
+        highlight: SourceSpan,
+        #[related]
+        related: Vec<MyRelated>,
+    }
+
+    #[derive(Debug, Diagnostic, Error)]
+    enum MyRelated {
+        #[error("oops!")]
+        #[diagnostic(
+            severity(Error),
+            code(oops::my::related::error),
+            help("try doing it better next time?")
+        )]
+        Error {
+            #[source_code]
+            src: NamedSource,
+            #[label("this bit here")]
+            highlight: SourceSpan,
+        },
+
+        #[error("oops!")]
+        #[diagnostic(
+            severity(Warning),
+            code(oops::my::related::warning),
+            help("try doing it better next time?")
+        )]
+        Warning {
+            #[source_code]
+            src: NamedSource,
+            #[label("this bit here")]
+            highlight: SourceSpan,
+        },
+
+        #[error("oops!")]
+        #[diagnostic(
+            severity(Advice),
+            code(oops::my::related::advice),
+            help("try doing it better next time?")
+        )]
+        Advice {
+            #[source_code]
+            src: NamedSource,
+            #[label("this bit here")]
+            highlight: SourceSpan,
+        },
+    }
+
+    let src = "source\n  text\n    here".to_string();
+    let err = MyBad {
+        src: NamedSource::new("bad_file.rs", src.clone()),
+        highlight: (9, 4).into(),
+        related: vec![
+            MyRelated::Error {
+                src: NamedSource::new("bad_file.rs", src.clone()),
+                highlight: (0, 6).into(),
+            },
+            MyRelated::Warning {
+                src: NamedSource::new("bad_file.rs", src.clone()),
+                highlight: (0, 6).into(),
+            },
+            MyRelated::Advice {
+                src: NamedSource::new("bad_file.rs", src),
+                highlight: (0, 6).into(),
+            },
+        ],
+    };
+    let out = fmt_report(err.into());
+    println!("Error: {}", out);
+    let expected = r#"oops::my::bad
+
+  × oops!
+   ╭─[bad_file.rs:1:1]
+ 1 │ source
+ 2 │   text
+   ·   ──┬─
+   ·     ╰── this bit here
+ 3 │     here
+   ╰────
+  help: try doing it better next time?
+
+Error: oops::my::related::error
+
+  × oops!
+   ╭─[bad_file.rs:1:1]
+ 1 │ source
+   · ───┬──
+   ·    ╰── this bit here
+ 2 │   text
+   ╰────
+  help: try doing it better next time?
+Warning: oops::my::related::warning
+
+  ⚠ oops!
+   ╭─[bad_file.rs:1:1]
+ 1 │ source
+   · ───┬──
+   ·    ╰── this bit here
+ 2 │   text
+   ╰────
+  help: try doing it better next time?
+Advice: oops::my::related::advice
+
+  ☞ oops!
+   ╭─[bad_file.rs:1:1]
+ 1 │ source
+   · ───┬──
+   ·    ╰── this bit here
+ 2 │   text
+   ╰────
+  help: try doing it better next time?
+"#
+    .trim_start()
+    .to_string();
+    assert_eq!(expected, out);
+    Ok(())
+}
+
 #[test]
 fn zero_length_eol_span() {
     #[derive(Error, Debug, Diagnostic)]