Rust: Poor error message when user forgets derive that has attributes

Created on 20 Jan 2018  路  9Comments  路  Source: rust-lang/rust

#[macro_use]
extern crate serde_derive;

#[serde(untagged)]
enum CellIndex {
    Auto,
    Index(u32),
}
error[E0658]: The attribute `serde` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642)
 --> src/main.rs:4:1
  |
4 | #[serde(untagged)]
  | ^^^^^^^^^^^^^^^^^^
  |
  = help: add #![feature(custom_attribute)] to the crate attributes to enable

The error and suggestion are super misleading and I have seen this a few times in #serde. It should be possible for the compiler to observe that there are derive macros in scope with serde declared as an attribute, and suggest using those.

// These are in scope
#[proc_macro_derive(Serialize, attributes(serde))]
#[proc_macro_derive(Deserialize, attributes(serde))]

A better message would not have the part about the compiler adding meaning to #[serde] in the future and would recommend using #[derive(Serialize)] or #[derive(Deserialize)] on the struct containing the attribute.

A-diagnostics A-macros A-proc-macros C-enhancement D-newcomer-roadblock T-compiler

Most helpful comment

There's a much more likely reproducer of case 2 on edition 2018, if you simply forget to use serde_derive::Serialize;

#[derive(Serialize)]
#[serde(untagged)]
enum CellIndex {
    Auto,
    Index(u32),
}

gives the same error and doesn't mention the missing derive at all

error[E0658]: The attribute `serde` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642)
 --> src/main.rs:2:3
  |
2 | #[serde(untagged)]
  |   ^^^^^
  |
  = help: add #![feature(custom_attribute)] to the crate attributes to enable

All 9 comments

Mentioning @keeperofdakeys who implemented attribute support for derives in #37614.
Mentioning @topecongiro who worked on derive macro improvements recently in #47013.

Would either of you be interested in following up with an improved error message for this case?

This error message could be a bit misleading. What if they really wanted a proc macro and forgot to import it? What if derives from multiple crates use the same attribute? What if the derive macro hasn't been imported?

It is a nice solution though, and I'd be interested in more views about it.

Edit: And I'm happy to implement this if required.

Irrespective of this, the generic error message should have something like "did you forget to import a proc macro, or define a #[derive] for this item?" added to it.

ping @jseyfried @nrc

What should the diagnostic text be for the following cases?:

  1. #[macro_use] extern crate serde_derive; (the presented case in the description): user forgot to use #[derive]
  2. extern crate serde_derive;: user forgot to add #[macro_use]
  3. user forgot to add the serde crate to the project in the code
  4. user forgot to add the serde crate to the toml config file

I can see how we can provide good diagnostics for cases 1 and 2, _maybe_ for case 3, but I'm intrigued by what the appropriate text for case 4 might be for newcomers, short of a whitelist of attributes from popular crates :-/

Your case 1 is the only one I have seen over and over again in IRC. I wouldn't bother with the other cases. For case 1 I would like a diagnostic like this:

error[E...]: The attribute `serde` is provided by derive(Serialize) or derive(Deserialize); one of these must be derived on `CellIndex` for this attribute to be available
 --> src/main.rs:4:1
  |
4 | #[serde(untagged)]
  | ^^^^^^^^^^^^^^^^^^

along with a suggested fix that shows adding the (I guess alphanumerically the first, if there is more than one in scope) derive as a new #[derive(Deserialize)] attribute above the type if there are not already any derives on that type, or inserting Deserialize into the list of derives if there is already a derive attribute.

There's a much more likely reproducer of case 2 on edition 2018, if you simply forget to use serde_derive::Serialize;

#[derive(Serialize)]
#[serde(untagged)]
enum CellIndex {
    Auto,
    Index(u32),
}

gives the same error and doesn't mention the missing derive at all

error[E0658]: The attribute `serde` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642)
 --> src/main.rs:2:3
  |
2 | #[serde(untagged)]
  |   ^^^^^
  |
  = help: add #![feature(custom_attribute)] to the crate attributes to enable

Yeah, this bit me hard yesterday.

It is especially difficult to figure out what happens when #[derive(De/Serialize) is in place, but something went wrong with the import. For example, the feature serde_derive is not specified for serde and there is some other issues in code, the error message is just about #[serde(...)]. Only after every possible simplification of the code and the correction of potential errors, a message appears that the derived trait is not a macro.

The error message now says that the derive macro is unresolved when #[derive(De/Serialize) is in place:

error[E0658]: The attribute `serde` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642)
 --> src/main.rs:2:3
  |
2 | #[serde(untagged)]
  |   ^^^^^
  |
  = help: add #![feature(custom_attribute)] to the crate attributes to enable

error: cannot find derive macro `Serialize` in this scope
 --> src/main.rs:1:10
  |
1 | #[derive(Serialize)]
  |          ^^^^^^^^^

This was fixed somewhere between stable and beta in one of PRs improving error recovery, https://github.com/rust-lang/rust/pull/56999 probably.

Had similar issue with Rust 2018 and this code:

#[derive(Serialize)] // where I forgot this line
#[serde(remote = "StatusCode")]
struct StatusCodeDef(#[serde(getter = "StatusCode::as_u16")] u16);

impl From<StatusCodeDef> for StatusCode {
    fn from(def: StatusCodeDef) -> Self {
        StatusCode::from_u16(def.0).unwrap()
    }
}

and the error was:

error: cannot find attribute `serde` in this scope
  --> src/error.rs:11:3
   |
11 | #[serde(remote = "StatusCode")]
   |   ^^^^^

error: cannot find attribute `serde` in this scope
  --> src/error.rs:12:24
   |
12 | struct StatusCodeDef(#[serde(getter = "StatusCode::as_u16")] u16);
   |                        ^^^^^
Was this page helpful?
0 / 5 - 0 ratings