shepmaster / snafu

Easily assign underlying errors into domain-specific errors while adding context
https://docs.rs/snafu/
Apache License 2.0
1.4k stars 60 forks source link

Confusing error message when attempting to use a context selector without a context #389

Open jmrgibson opened 1 year ago

jmrgibson commented 1 year ago

When attempting to use a generated context selector in a macro like ensure! that doesn't provide a context, the error message is quite confusing if the context selector has fields like "source".

For example

use snafu::prelude::*;

#[derive(Debug, Snafu)]
pub enum Error {
    Foo {
        source: std::io::Error,  // this is the problematic line
        id: usize,
    }
}

fn main() {
    ensure!(1 == 1, FooSnafu{id: 10})
}

gives the error message

error[E0599]: no method named `fail` found for struct `FooSnafu` in the current scope
  --> src/main.rs:12:5
   |
3  | #[derive(Debug, Snafu)]
   |                 ----- method `fail` not found for this struct
...
12 |     ensure!(1 == 1, FooSnafu{id: 10})
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `FooSnafu<{integer}>`
   |
   = note: this error originates in the macro `ensure` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0599`.
error: could not compile `snafu-error-example` due to previous error
shepmaster commented 1 year ago

Hmm. I'm honestly not very sure what we could do to improve this case. The ensure macro basically expands to $selector.fail() and thus you get the error seen.

It's possible that a combination of two things could help:

If those two things existed, the macro could be changed to expand to TheBuildTrait::fail($selector) and we could put #[diagnostic::on_unimplemented] on TheBuildTrait. That might allow us to have an error like "fail is only available for context selectors that do not have a source".

If you had seen something along those lines added to the error you already got, do you think it would have helped?

error[E0599]: no method named `fail` found for struct `FooSnafu` in the current scope
  --> src/main.rs:12:5
   |
3  | #[derive(Debug, Snafu)]
   |                 ----- method `fail` not found for this struct
...
12 |     ensure!(1 == 1, FooSnafu{id: 10})
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `FooSnafu<{integer}>`
   |
   = help: the trait `TheBuildTrait` is not implemented for context selectors that do not have a source