Poor error message when user forgets derive that has attributes

87ffa99
Opened by David Tolnay at 2023-03-17 23:05:06
#[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.

  1. 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?

    David Tolnay at 2018-01-20 05:57:24

  2. 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

    keeperofdakeys at 2018-01-20 21:40:57

  3. 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 :-/

    Esteban Kuber at 2018-07-19 17:51:03

  4. 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.

    David Tolnay at 2018-07-19 18:05:36

  5. 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
    

    Nemo157 at 2018-10-05 09:28:40

  6. Yeah, this bit me hard yesterday.

    Steve Klabnik at 2018-10-05 14:36:52

  7. 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.

    Alexander Irbis at 2019-01-29 10:16:21

  8. 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.

    Vadim Petrochenkov at 2019-01-29 10:23:13

  9. 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);
       |                        ^^^^^
    

    Paul-Sebastian Manole at 2020-02-09 23:11:41

  10. I'm working on a PR for this <img width="1026" alt="Screenshot 2023-03-17 at 11 25 35 AM" src="https://user-images.githubusercontent.com/1606434/225988757-9ef8077e-b3b9-45f6-8ebf-0a0ea104e9da.png">

    error: cannot find attribute `sede` in this scope
      --> src/main.rs:18:7
       |
    18 |     #[sede(untagged)]
       |       ^^^^
       |
    help: the derive macros `Serialize` and `Deserialize` accept the similarly named `serde` attribute
       |
    18 |     #[serde(untagged)]
       |       ~~~~~
    
    error: cannot find attribute `serde` in this scope
      --> src/main.rs:12:7
       |
    12 |     #[serde(untagged)]
       |       ^^^^^
       |
       = note: `serde` is in scope, but it is a crate, not an attribute
    help: `serde` is an attribute that can be used by the derive macros `Serialize` and `Deserialize`, you might be missing a `derive` attribute
       |
    10 | #[derive(Serialize, Deserialize)]
       |
    

    For now it only works if use serde::Serialize; is present.

    Edit:

    The suggestion will now happen as long as at least use serde; is involved somewhere in the crate. I feel like that might be enough for unblocking most people.

    Esteban Kuber at 2023-03-17 18:31:20