dtolnay / proc-macro-workshop

Learn to write Rust procedural macros  [Rust Latam conference, Montevideo Uruguay, March 2019]
Apache License 2.0
4.13k stars 1.04k forks source link

`derive(Builder)` : Unable to generate the correct error message. #21

Closed rajivr closed 5 years ago

rajivr commented 5 years ago

When I try to do cargo expand tests/08-unrecognized-attribute.rs (copied as main.rs), instead of getting the error message (expected builder(each = "...")) created by syn::Error::new_spanned(...) in field_attr_builder_each, for some reason the error message expected one of ... is being generated.

error: expected `:`, found `!`
  --> main.rs:22:7
   |
22 |     #[builder(eac = "arg")]
   |       ^^^^^^^ expected `:`
error: expected one of `,` or `}`, found `!`
  --> main.rs:22:7
   |
20 | pub struct Command {
   |            ------- while parsing this struct
21 |     executable: String,
22 |     #[builder(eac = "arg")]
   |       ^^^^^^^ expected one of `,` or `}` here
error: aborting due to 2 previous errors
error: Could not compile `proc-macro-workshop`.
To learn more, run the command again with --verbose.

I can't seem to understand where this error message is originating from, and why the token stream from err.to_compile_error() is not being displayed instead.

I would greatly appreciate any pointers on how I can solve this! :-)

rajivr commented 5 years ago

The output tokens are likely invalid syntax.

Thanks David for the pointer. :-) It looks like I had misunderstood where to insert the to_compile_error() token stream.

Is there a better way to bubble up errors from nested closures rather than doing something like below?

Right now I am first checking to make sure there are no errors from field_attr_builder_each() and then in the subsequent use of fields.iter(), use unwrap().

Ideally I would have wanted a way to get a Result<some iterator, syn::Error> from fields.iter(), but was unable to make that happen.

    // Deal with potential error in `field_attr_builder_each()`
    if let Err(e) = fields.iter().try_for_each(|f| {
        if field_attr_builder_each(f).is_some() {
            field_attr_builder_each(f)
                .unwrap()
                .map(|_| Ok(()))
                .unwrap_or_else(|err| Err(err))
        } else {
            Ok(())
        }
    }) {
        return e.to_compile_error().into();
    }
dtolnay commented 5 years ago

The main way to go from Iterator of Result to Result of Iterator is with collect:

let out = data.iter()
    .map(|elem| {
        /* Ok or Err */
    })
    .collect::<Result<Vec<_>>()?;

println!("{:?}", out); // Vec<_>
rajivr commented 5 years ago

Thanks @dtolnay.