rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.24k stars 12.7k forks source link

Invalid suggestion when using `serde` `serialize_with` #129756

Open cyrgani opened 2 months ago

cyrgani commented 2 months ago

Code

use serde::{Serialize, Serializer};

fn f<T, S: Serializer>(_: &(), _: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> {
    todo!()
}

#[derive(Serialize)]
pub struct Matrix {
    #[serde(serialize_with = "f")]
    matrix: (),
}

Current output

error[E0282]: type annotations needed
 --> src/lib.rs:9:30
  |
9 |     #[serde(serialize_with = "f")]
  |                              ^^^ cannot infer type of the type parameter `T` declared on the function `f`
  |
help: consider specifying the generic arguments
  |
9 |     #[serde(serialize_with = "f"::<_, __S>)]
  |                                 ++++++++++

Desired output

error[E0282]: type annotations needed
 --> src/lib.rs:9:30
  |
9 |     #[serde(serialize_with = "f")]
  |                              ^^^ cannot infer type of the type parameter `T` declared on the function `f`

Rationale and extra context

Inspired by #120922. The given suggestion is invalid syntax.

Other cases

No response

Rust Version

rustc 1.82.0 playground

Anything else?

No response

cyrgani commented 1 month ago

A reduced version:

// crate `serde_derive`
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, spanned::Spanned, DeriveInput, Expr, ExprPath, LitStr};

#[proc_macro_derive(Serialize, attributes(serde))]
pub fn derive_serialize(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let syn::Data::Struct(data_struct) = &input.data else {
        unreachable!()
    };
    let syn::Fields::Named(fields) = &data_struct.fields else {
        unreachable!()
    };

    let field = &fields.named[0];
    let mut serialize_with = None;

    field.attrs[0]
        .parse_nested_meta(|meta| {
            let expr = meta.value().unwrap().parse::<Expr>().unwrap();
            let lit = LitStr::new("f", expr.span());
            let path = lit.parse::<ExprPath>().unwrap();
            serialize_with = Some(path);
            Ok(())
        })
        .unwrap();

    let path = serialize_with.unwrap();

    quote! {
        fn serialize() {
            #path();
        }
    }
    .into()
}
// consuming crate
use serde_derive::Serialize;

fn f<T>() {}

#[derive(Serialize)]
pub struct Matrix {
    #[serde(serialize_with = "f")]
    matrix: (),
}

This produces:

error[E0282]: type annotations needed
 --> src/lib.rs:7:30
  |
7 |     #[serde(serialize_with = "f")]
  |                              ^^^ cannot infer type of the type parameter `T` declared on the function `f`
  |
help: consider specifying the generic argument
  |
7 |     #[serde(serialize_with = "f"::<_>)]
  |                                 +++++