From 9144a8846645be1a68ecfd0817f6f87867e6be0e Mon Sep 17 00:00:00 2001 From: Veetaha Date: Sat, 19 Oct 2024 19:11:35 +0000 Subject: [PATCH 1/5] Make compile error message point to field type instead of call site for not implemented Serialize trait --- serde_derive/src/ser.rs | 37 ++-- .../fn_param.rs} | 0 .../fn_param.stderr} | 8 +- .../tests/ui/on-unimplemented/struct_field.rs | 11 ++ .../ui/on-unimplemented/struct_field.stderr | 166 ++++++++++++++++++ .../ui/on-unimplemented/struct_variant.rs | 10 ++ .../ui/on-unimplemented/struct_variant.stderr | 108 ++++++++++++ .../tests/ui/on-unimplemented/tuple_field.rs | 8 + .../ui/on-unimplemented/tuple_field.stderr | 82 +++++++++ .../ui/on-unimplemented/tuple_variant.rs | 10 ++ .../ui/on-unimplemented/tuple_variant.stderr | 53 ++++++ 11 files changed, 477 insertions(+), 16 deletions(-) rename test_suite/tests/ui/{on_unimplemented.rs => on-unimplemented/fn_param.rs} (100%) rename test_suite/tests/ui/{on_unimplemented.stderr => on-unimplemented/fn_param.stderr} (90%) create mode 100644 test_suite/tests/ui/on-unimplemented/struct_field.rs create mode 100644 test_suite/tests/ui/on-unimplemented/struct_field.stderr create mode 100644 test_suite/tests/ui/on-unimplemented/struct_variant.rs create mode 100644 test_suite/tests/ui/on-unimplemented/struct_variant.stderr create mode 100644 test_suite/tests/ui/on-unimplemented/tuple_field.rs create mode 100644 test_suite/tests/ui/on-unimplemented/tuple_field.stderr create mode 100644 test_suite/tests/ui/on-unimplemented/tuple_variant.rs create mode 100644 test_suite/tests/ui/on-unimplemented/tuple_variant.stderr diff --git a/serde_derive/src/ser.rs b/serde_derive/src/ser.rs index 35f8ca4bd..0e1a62488 100644 --- a/serde_derive/src/ser.rs +++ b/serde_derive/src/ser.rs @@ -311,7 +311,7 @@ fn serialize_struct_tag_field(cattrs: &attr::Container, struct_trait: &StructTra match cattrs.tag() { attr::TagType::Internal { tag } => { let type_name = cattrs.name().serialize_name(); - let func = struct_trait.serialize_field(Span::call_site()); + let func = struct_trait.serialize_field(None); quote! { #func(&mut __serde_state, #tag, #type_name)?; } @@ -1081,8 +1081,13 @@ fn serialize_tuple_struct_visitor( field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr); } - let span = field.original.span(); - let func = tuple_trait.serialize_element(span); + let ty = if field.attrs.serialize_with().is_none() { + Some(field.ty) + } else { + None + }; + + let func = tuple_trait.serialize_element(ty); let ser = quote! { #func(&mut __serde_state, #field_expr)?; }; @@ -1131,7 +1136,13 @@ fn serialize_struct_visitor( #func(&#field_expr, _serde::__private::ser::FlatMapSerializer(&mut __serde_state))?; } } else { - let func = struct_trait.serialize_field(span); + let ty = if field.attrs.serialize_with().is_none() { + Some(field.ty) + } else { + None + }; + + let func = struct_trait.serialize_field(ty); quote! { #func(&mut __serde_state, #key_expr, #field_expr)?; } @@ -1300,16 +1311,17 @@ enum StructTrait { } impl StructTrait { - fn serialize_field(&self, span: Span) -> TokenStream { + fn serialize_field(&self, ty: Option<&syn::Type>) -> TokenStream { + let ty = ty.into_iter(); match *self { StructTrait::SerializeMap => { - quote_spanned!(span=> _serde::ser::SerializeMap::serialize_entry) + quote!(_serde::ser::SerializeMap::serialize_entry #( ::<_, #ty> )* ) } StructTrait::SerializeStruct => { - quote_spanned!(span=> _serde::ser::SerializeStruct::serialize_field) + quote!(_serde::ser::SerializeStruct::serialize_field #( ::<#ty> )* ) } StructTrait::SerializeStructVariant => { - quote_spanned!(span=> _serde::ser::SerializeStructVariant::serialize_field) + quote!(_serde::ser::SerializeStructVariant::serialize_field #( ::<#ty> )* ) } } } @@ -1334,16 +1346,17 @@ enum TupleTrait { } impl TupleTrait { - fn serialize_element(&self, span: Span) -> TokenStream { + fn serialize_element(&self, ty: Option<&syn::Type>) -> TokenStream { + let ty = ty.into_iter(); match *self { TupleTrait::SerializeTuple => { - quote_spanned!(span=> _serde::ser::SerializeTuple::serialize_element) + quote!(_serde::ser::SerializeTuple::serialize_element #( ::<#ty> )*) } TupleTrait::SerializeTupleStruct => { - quote_spanned!(span=> _serde::ser::SerializeTupleStruct::serialize_field) + quote!(_serde::ser::SerializeTupleStruct::serialize_field #( ::<#ty> )*) } TupleTrait::SerializeTupleVariant => { - quote_spanned!(span=> _serde::ser::SerializeTupleVariant::serialize_field) + quote!(_serde::ser::SerializeTupleVariant::serialize_field #( ::<#ty> )*) } } } diff --git a/test_suite/tests/ui/on_unimplemented.rs b/test_suite/tests/ui/on-unimplemented/fn_param.rs similarity index 100% rename from test_suite/tests/ui/on_unimplemented.rs rename to test_suite/tests/ui/on-unimplemented/fn_param.rs diff --git a/test_suite/tests/ui/on_unimplemented.stderr b/test_suite/tests/ui/on-unimplemented/fn_param.stderr similarity index 90% rename from test_suite/tests/ui/on_unimplemented.stderr rename to test_suite/tests/ui/on-unimplemented/fn_param.stderr index 1b0318190..bd00f6041 100644 --- a/test_suite/tests/ui/on_unimplemented.stderr +++ b/test_suite/tests/ui/on-unimplemented/fn_param.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `MyStruct: Serialize` is not satisfied - --> tests/ui/on_unimplemented.rs:21:15 + --> tests/ui/on-unimplemented/fn_param.rs:21:15 | 21 | to_string(&MyStruct); | --------- ^^^^^^^^^ the trait `Serialize` is not implemented for `MyStruct` @@ -19,7 +19,7 @@ error[E0277]: the trait bound `MyStruct: Serialize` is not satisfied (T0, T1, T2, T3, T4) and $N others note: required by a bound in `to_string` - --> tests/ui/on_unimplemented.rs:6:8 + --> tests/ui/on-unimplemented/fn_param.rs:6:8 | 4 | fn to_string(_: &T) -> String | --------- required by a bound in this function @@ -28,7 +28,7 @@ note: required by a bound in `to_string` | ^^^^^^^^^ required by this bound in `to_string` error[E0277]: the trait bound `MyStruct: Deserialize<'_>` is not satisfied - --> tests/ui/on_unimplemented.rs:22:23 + --> tests/ui/on-unimplemented/fn_param.rs:22:23 | 22 | let _: MyStruct = from_str(""); | ^^^^^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `MyStruct` @@ -46,7 +46,7 @@ error[E0277]: the trait bound `MyStruct: Deserialize<'_>` is not satisfied (T0, T1, T2, T3) and $N others note: required by a bound in `from_str` - --> tests/ui/on_unimplemented.rs:13:8 + --> tests/ui/on-unimplemented/fn_param.rs:13:8 | 11 | fn from_str<'de, T>(_: &'de str) -> T | -------- required by a bound in this function diff --git a/test_suite/tests/ui/on-unimplemented/struct_field.rs b/test_suite/tests/ui/on-unimplemented/struct_field.rs new file mode 100644 index 000000000..a1c20513e --- /dev/null +++ b/test_suite/tests/ui/on-unimplemented/struct_field.rs @@ -0,0 +1,11 @@ +use serde_derive::{Deserialize, Serialize}; + +struct NoImpls; + +#[derive(Serialize, Deserialize)] +struct S { + x1: u32, + x2: NoImpls, +} + +fn main() {} diff --git a/test_suite/tests/ui/on-unimplemented/struct_field.stderr b/test_suite/tests/ui/on-unimplemented/struct_field.stderr new file mode 100644 index 000000000..c4dfe780f --- /dev/null +++ b/test_suite/tests/ui/on-unimplemented/struct_field.stderr @@ -0,0 +1,166 @@ +error[E0277]: the trait bound `NoImpls: Serialize` is not satisfied + --> tests/ui/on-unimplemented/struct_field.rs:8:9 + | +8 | x2: NoImpls, + | ^^^^^^^ the trait `Serialize` is not implemented for `NoImpls` + | + = note: for local types consider adding `#[derive(serde::Serialize)]` to your `NoImpls` type + = note: for types from other crates check whether the crate offers a `serde` feature flag + = help: the following other types implement trait `Serialize`: + &'a T + &'a mut T + () + (T,) + (T0, T1) + (T0, T1, T2) + (T0, T1, T2, T3) + (T0, T1, T2, T3, T4) + and $N others +note: required by a bound in `_::_serde::ser::SerializeStruct::serialize_field` + --> $WORKSPACE/serde/src/ser/mod.rs + | + | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> + | --------------- required by a bound in this associated function + | where + | T: ?Sized + Serialize; + | ^^^^^^^^^ required by this bound in `SerializeStruct::serialize_field` + +error[E0277]: the trait bound `NoImpls: Deserialize<'_>` is not satisfied + --> tests/ui/on-unimplemented/struct_field.rs:8:9 + | +8 | x2: NoImpls, + | ^^^^^^^ the trait `Deserialize<'_>` is not implemented for `NoImpls` + | + = note: for local types consider adding `#[derive(serde::Deserialize)]` to your `NoImpls` type + = note: for types from other crates check whether the crate offers a `serde` feature flag + = help: the following other types implement trait `Deserialize<'de>`: + &'a Path + &'a [u8] + &'a str + () + (T,) + (T0, T1) + (T0, T1, T2) + (T0, T1, T2, T3) + and $N others +note: required by a bound in `next_element` + --> $WORKSPACE/serde/src/de/mod.rs + | + | fn next_element(&mut self) -> Result, Self::Error> + | ------------ required by a bound in this associated function + | where + | T: Deserialize<'de>, + | ^^^^^^^^^^^^^^^^ required by this bound in `SeqAccess::next_element` + +error[E0277]: the trait bound `NoImpls: Deserialize<'_>` is not satisfied + --> tests/ui/on-unimplemented/struct_field.rs:8:9 + | +8 | x2: NoImpls, + | ^^^^^^^ the trait `Deserialize<'_>` is not implemented for `NoImpls` + | + = note: for local types consider adding `#[derive(serde::Deserialize)]` to your `NoImpls` type + = note: for types from other crates check whether the crate offers a `serde` feature flag + = help: the following other types implement trait `Deserialize<'de>`: + &'a Path + &'a [u8] + &'a str + () + (T,) + (T0, T1) + (T0, T1, T2) + (T0, T1, T2, T3) + and $N others +note: required by a bound in `next_value` + --> $WORKSPACE/serde/src/de/mod.rs + | + | fn next_value(&mut self) -> Result + | ---------- required by a bound in this associated function + | where + | V: Deserialize<'de>, + | ^^^^^^^^^^^^^^^^ required by this bound in `MapAccess::next_value` + +error[E0277]: the trait bound `NoImpls: Deserialize<'_>` is not satisfied + --> tests/ui/on-unimplemented/struct_field.rs:5:21 + | +5 | #[derive(Serialize, Deserialize)] + | ^^^^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `NoImpls` + | + = note: for local types consider adding `#[derive(serde::Deserialize)]` to your `NoImpls` type + = note: for types from other crates check whether the crate offers a `serde` feature flag + = help: the following other types implement trait `Deserialize<'de>`: + &'a Path + &'a [u8] + &'a str + () + (T,) + (T0, T1) + (T0, T1, T2) + (T0, T1, T2, T3) + and $N others +note: required by a bound in `_::_serde::__private::de::missing_field` + --> $WORKSPACE/serde/src/private/de.rs + | + | pub fn missing_field<'de, V, E>(field: &'static str) -> Result + | ------------- required by a bound in this function + | where + | V: Deserialize<'de>, + | ^^^^^^^^^^^^^^^^ required by this bound in `missing_field` + = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NoImpls: Deserialize<'_>` is not satisfied + --> tests/ui/on-unimplemented/struct_field.rs:5:21 + | +5 | #[derive(Serialize, Deserialize)] + | ^^^^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `NoImpls`, which is required by `InPlaceSeed<'_, NoImpls>: DeserializeSeed<'_>` + | + = note: for local types consider adding `#[derive(serde::Deserialize)]` to your `NoImpls` type + = note: for types from other crates check whether the crate offers a `serde` feature flag + = help: the following other types implement trait `Deserialize<'de>`: + &'a Path + &'a [u8] + &'a str + () + (T,) + (T0, T1) + (T0, T1, T2) + (T0, T1, T2, T3) + and $N others + = note: required for `InPlaceSeed<'_, NoImpls>` to implement `DeserializeSeed<'_>` +note: required by a bound in `next_element_seed` + --> $WORKSPACE/serde/src/de/mod.rs + | + | fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> + | ----------------- required by a bound in this associated function + | where + | T: DeserializeSeed<'de>; + | ^^^^^^^^^^^^^^^^^^^^ required by this bound in `SeqAccess::next_element_seed` + = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NoImpls: Deserialize<'_>` is not satisfied + --> tests/ui/on-unimplemented/struct_field.rs:5:21 + | +5 | #[derive(Serialize, Deserialize)] + | ^^^^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `NoImpls`, which is required by `InPlaceSeed<'_, NoImpls>: DeserializeSeed<'_>` + | + = note: for local types consider adding `#[derive(serde::Deserialize)]` to your `NoImpls` type + = note: for types from other crates check whether the crate offers a `serde` feature flag + = help: the following other types implement trait `Deserialize<'de>`: + &'a Path + &'a [u8] + &'a str + () + (T,) + (T0, T1) + (T0, T1, T2) + (T0, T1, T2, T3) + and $N others + = note: required for `InPlaceSeed<'_, NoImpls>` to implement `DeserializeSeed<'_>` +note: required by a bound in `next_value_seed` + --> $WORKSPACE/serde/src/de/mod.rs + | + | fn next_value_seed(&mut self, seed: V) -> Result + | --------------- required by a bound in this associated function + | where + | V: DeserializeSeed<'de>; + | ^^^^^^^^^^^^^^^^^^^^ required by this bound in `MapAccess::next_value_seed` + = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/test_suite/tests/ui/on-unimplemented/struct_variant.rs b/test_suite/tests/ui/on-unimplemented/struct_variant.rs new file mode 100644 index 000000000..a4abfb60e --- /dev/null +++ b/test_suite/tests/ui/on-unimplemented/struct_variant.rs @@ -0,0 +1,10 @@ +use serde_derive::{Deserialize, Serialize}; + +struct NoImpls; + +#[derive(Serialize, Deserialize)] +enum E { + S { x1: u32, x2: NoImpls }, +} + +fn main() {} diff --git a/test_suite/tests/ui/on-unimplemented/struct_variant.stderr b/test_suite/tests/ui/on-unimplemented/struct_variant.stderr new file mode 100644 index 000000000..8a262b211 --- /dev/null +++ b/test_suite/tests/ui/on-unimplemented/struct_variant.stderr @@ -0,0 +1,108 @@ +error[E0277]: the trait bound `NoImpls: Serialize` is not satisfied + --> tests/ui/on-unimplemented/struct_variant.rs:7:22 + | +7 | S { x1: u32, x2: NoImpls }, + | ^^^^^^^ the trait `Serialize` is not implemented for `NoImpls` + | + = note: for local types consider adding `#[derive(serde::Serialize)]` to your `NoImpls` type + = note: for types from other crates check whether the crate offers a `serde` feature flag + = help: the following other types implement trait `Serialize`: + &'a T + &'a mut T + () + (T,) + (T0, T1) + (T0, T1, T2) + (T0, T1, T2, T3) + (T0, T1, T2, T3, T4) + and $N others +note: required by a bound in `_::_serde::ser::SerializeStructVariant::serialize_field` + --> $WORKSPACE/serde/src/ser/mod.rs + | + | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> + | --------------- required by a bound in this associated function + | where + | T: ?Sized + Serialize; + | ^^^^^^^^^ required by this bound in `SerializeStructVariant::serialize_field` + +error[E0277]: the trait bound `NoImpls: Deserialize<'_>` is not satisfied + --> tests/ui/on-unimplemented/struct_variant.rs:7:22 + | +7 | S { x1: u32, x2: NoImpls }, + | ^^^^^^^ the trait `Deserialize<'_>` is not implemented for `NoImpls` + | + = note: for local types consider adding `#[derive(serde::Deserialize)]` to your `NoImpls` type + = note: for types from other crates check whether the crate offers a `serde` feature flag + = help: the following other types implement trait `Deserialize<'de>`: + &'a Path + &'a [u8] + &'a str + () + (T,) + (T0, T1) + (T0, T1, T2) + (T0, T1, T2, T3) + and $N others +note: required by a bound in `next_element` + --> $WORKSPACE/serde/src/de/mod.rs + | + | fn next_element(&mut self) -> Result, Self::Error> + | ------------ required by a bound in this associated function + | where + | T: Deserialize<'de>, + | ^^^^^^^^^^^^^^^^ required by this bound in `SeqAccess::next_element` + +error[E0277]: the trait bound `NoImpls: Deserialize<'_>` is not satisfied + --> tests/ui/on-unimplemented/struct_variant.rs:7:22 + | +7 | S { x1: u32, x2: NoImpls }, + | ^^^^^^^ the trait `Deserialize<'_>` is not implemented for `NoImpls` + | + = note: for local types consider adding `#[derive(serde::Deserialize)]` to your `NoImpls` type + = note: for types from other crates check whether the crate offers a `serde` feature flag + = help: the following other types implement trait `Deserialize<'de>`: + &'a Path + &'a [u8] + &'a str + () + (T,) + (T0, T1) + (T0, T1, T2) + (T0, T1, T2, T3) + and $N others +note: required by a bound in `next_value` + --> $WORKSPACE/serde/src/de/mod.rs + | + | fn next_value(&mut self) -> Result + | ---------- required by a bound in this associated function + | where + | V: Deserialize<'de>, + | ^^^^^^^^^^^^^^^^ required by this bound in `MapAccess::next_value` + +error[E0277]: the trait bound `NoImpls: Deserialize<'_>` is not satisfied + --> tests/ui/on-unimplemented/struct_variant.rs:5:21 + | +5 | #[derive(Serialize, Deserialize)] + | ^^^^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `NoImpls` + | + = note: for local types consider adding `#[derive(serde::Deserialize)]` to your `NoImpls` type + = note: for types from other crates check whether the crate offers a `serde` feature flag + = help: the following other types implement trait `Deserialize<'de>`: + &'a Path + &'a [u8] + &'a str + () + (T,) + (T0, T1) + (T0, T1, T2) + (T0, T1, T2, T3) + and $N others +note: required by a bound in `_::_serde::__private::de::missing_field` + --> $WORKSPACE/serde/src/private/de.rs + | + | pub fn missing_field<'de, V, E>(field: &'static str) -> Result + | ------------- required by a bound in this function + | where + | V: Deserialize<'de>, + | ^^^^^^^^^^^^^^^^ required by this bound in `missing_field` + = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/test_suite/tests/ui/on-unimplemented/tuple_field.rs b/test_suite/tests/ui/on-unimplemented/tuple_field.rs new file mode 100644 index 000000000..2786c1c94 --- /dev/null +++ b/test_suite/tests/ui/on-unimplemented/tuple_field.rs @@ -0,0 +1,8 @@ +use serde_derive::{Deserialize, Serialize}; + +struct NoImpls; + +#[derive(Serialize, Deserialize)] +struct S(u32, NoImpls); + +fn main() {} diff --git a/test_suite/tests/ui/on-unimplemented/tuple_field.stderr b/test_suite/tests/ui/on-unimplemented/tuple_field.stderr new file mode 100644 index 000000000..cce800c1c --- /dev/null +++ b/test_suite/tests/ui/on-unimplemented/tuple_field.stderr @@ -0,0 +1,82 @@ +error[E0277]: the trait bound `NoImpls: Serialize` is not satisfied + --> tests/ui/on-unimplemented/tuple_field.rs:6:15 + | +6 | struct S(u32, NoImpls); + | ^^^^^^^ the trait `Serialize` is not implemented for `NoImpls` + | + = note: for local types consider adding `#[derive(serde::Serialize)]` to your `NoImpls` type + = note: for types from other crates check whether the crate offers a `serde` feature flag + = help: the following other types implement trait `Serialize`: + &'a T + &'a mut T + () + (T,) + (T0, T1) + (T0, T1, T2) + (T0, T1, T2, T3) + (T0, T1, T2, T3, T4) + and $N others +note: required by a bound in `_::_serde::ser::SerializeTupleStruct::serialize_field` + --> $WORKSPACE/serde/src/ser/mod.rs + | + | fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> + | --------------- required by a bound in this associated function + | where + | T: ?Sized + Serialize; + | ^^^^^^^^^ required by this bound in `SerializeTupleStruct::serialize_field` + +error[E0277]: the trait bound `NoImpls: Deserialize<'_>` is not satisfied + --> tests/ui/on-unimplemented/tuple_field.rs:6:15 + | +6 | struct S(u32, NoImpls); + | ^^^^^^^ the trait `Deserialize<'_>` is not implemented for `NoImpls` + | + = note: for local types consider adding `#[derive(serde::Deserialize)]` to your `NoImpls` type + = note: for types from other crates check whether the crate offers a `serde` feature flag + = help: the following other types implement trait `Deserialize<'de>`: + &'a Path + &'a [u8] + &'a str + () + (T,) + (T0, T1) + (T0, T1, T2) + (T0, T1, T2, T3) + and $N others +note: required by a bound in `next_element` + --> $WORKSPACE/serde/src/de/mod.rs + | + | fn next_element(&mut self) -> Result, Self::Error> + | ------------ required by a bound in this associated function + | where + | T: Deserialize<'de>, + | ^^^^^^^^^^^^^^^^ required by this bound in `SeqAccess::next_element` + +error[E0277]: the trait bound `NoImpls: Deserialize<'_>` is not satisfied + --> tests/ui/on-unimplemented/tuple_field.rs:5:21 + | +5 | #[derive(Serialize, Deserialize)] + | ^^^^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `NoImpls`, which is required by `InPlaceSeed<'_, NoImpls>: DeserializeSeed<'_>` + | + = note: for local types consider adding `#[derive(serde::Deserialize)]` to your `NoImpls` type + = note: for types from other crates check whether the crate offers a `serde` feature flag + = help: the following other types implement trait `Deserialize<'de>`: + &'a Path + &'a [u8] + &'a str + () + (T,) + (T0, T1) + (T0, T1, T2) + (T0, T1, T2, T3) + and $N others + = note: required for `InPlaceSeed<'_, NoImpls>` to implement `DeserializeSeed<'_>` +note: required by a bound in `next_element_seed` + --> $WORKSPACE/serde/src/de/mod.rs + | + | fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> + | ----------------- required by a bound in this associated function + | where + | T: DeserializeSeed<'de>; + | ^^^^^^^^^^^^^^^^^^^^ required by this bound in `SeqAccess::next_element_seed` + = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/test_suite/tests/ui/on-unimplemented/tuple_variant.rs b/test_suite/tests/ui/on-unimplemented/tuple_variant.rs new file mode 100644 index 000000000..71c43ca5d --- /dev/null +++ b/test_suite/tests/ui/on-unimplemented/tuple_variant.rs @@ -0,0 +1,10 @@ +use serde_derive::{Deserialize, Serialize}; + +struct NoImpls; + +#[derive(Serialize, Deserialize)] +enum E { + S(u32, NoImpls) +} + +fn main() {} diff --git a/test_suite/tests/ui/on-unimplemented/tuple_variant.stderr b/test_suite/tests/ui/on-unimplemented/tuple_variant.stderr new file mode 100644 index 000000000..92807aed8 --- /dev/null +++ b/test_suite/tests/ui/on-unimplemented/tuple_variant.stderr @@ -0,0 +1,53 @@ +error[E0277]: the trait bound `NoImpls: Serialize` is not satisfied + --> tests/ui/on-unimplemented/tuple_variant.rs:7:12 + | +7 | S(u32, NoImpls) + | ^^^^^^^ the trait `Serialize` is not implemented for `NoImpls` + | + = note: for local types consider adding `#[derive(serde::Serialize)]` to your `NoImpls` type + = note: for types from other crates check whether the crate offers a `serde` feature flag + = help: the following other types implement trait `Serialize`: + &'a T + &'a mut T + () + (T,) + (T0, T1) + (T0, T1, T2) + (T0, T1, T2, T3) + (T0, T1, T2, T3, T4) + and $N others +note: required by a bound in `_::_serde::ser::SerializeTupleVariant::serialize_field` + --> $WORKSPACE/serde/src/ser/mod.rs + | + | fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> + | --------------- required by a bound in this associated function + | where + | T: ?Sized + Serialize; + | ^^^^^^^^^ required by this bound in `SerializeTupleVariant::serialize_field` + +error[E0277]: the trait bound `NoImpls: Deserialize<'_>` is not satisfied + --> tests/ui/on-unimplemented/tuple_variant.rs:7:12 + | +7 | S(u32, NoImpls) + | ^^^^^^^ the trait `Deserialize<'_>` is not implemented for `NoImpls` + | + = note: for local types consider adding `#[derive(serde::Deserialize)]` to your `NoImpls` type + = note: for types from other crates check whether the crate offers a `serde` feature flag + = help: the following other types implement trait `Deserialize<'de>`: + &'a Path + &'a [u8] + &'a str + () + (T,) + (T0, T1) + (T0, T1, T2) + (T0, T1, T2, T3) + and $N others +note: required by a bound in `next_element` + --> $WORKSPACE/serde/src/de/mod.rs + | + | fn next_element(&mut self) -> Result, Self::Error> + | ------------ required by a bound in this associated function + | where + | T: Deserialize<'de>, + | ^^^^^^^^^^^^^^^^ required by this bound in `SeqAccess::next_element` From 6cac7554165b9550ee9d79100ab59f527dd85905 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Mon, 21 Oct 2024 22:32:33 +0000 Subject: [PATCH 2/5] Return back `quote_spanned` for the rest of `serialize_entry/field/element` expression. Extract a shared function --- serde_derive/src/ser.rs | 71 +++++++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/serde_derive/src/ser.rs b/serde_derive/src/ser.rs index 0e1a62488..c8eddd70e 100644 --- a/serde_derive/src/ser.rs +++ b/serde_derive/src/ser.rs @@ -1081,13 +1081,7 @@ fn serialize_tuple_struct_visitor( field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr); } - let ty = if field.attrs.serialize_with().is_none() { - Some(field.ty) - } else { - None - }; - - let func = tuple_trait.serialize_element(ty); + let func = tuple_trait.serialize_element(field); let ser = quote! { #func(&mut __serde_state, #field_expr)?; }; @@ -1111,6 +1105,7 @@ fn serialize_struct_visitor( .filter(|&field| !field.attrs.skip_serializing()) .map(|field| { let member = &field.member; + let span = field.original.span(); let mut field_expr = if is_enum { quote!(#member) @@ -1129,20 +1124,14 @@ fn serialize_struct_visitor( field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr); } - let span = field.original.span(); let ser = if field.attrs.flatten() { let func = quote_spanned!(span=> _serde::Serialize::serialize); quote! { #func(&#field_expr, _serde::__private::ser::FlatMapSerializer(&mut __serde_state))?; } } else { - let ty = if field.attrs.serialize_with().is_none() { - Some(field.ty) - } else { - None - }; + let func = struct_trait.serialize_field(Some(field)); - let func = struct_trait.serialize_field(ty); quote! { #func(&mut __serde_state, #key_expr, #field_expr)?; } @@ -1311,17 +1300,28 @@ enum StructTrait { } impl StructTrait { - fn serialize_field(&self, ty: Option<&syn::Type>) -> TokenStream { - let ty = ty.into_iter(); + fn serialize_field(&self, field: Option<&Field>) -> TokenStream { + let span = field + .map(|field| field.original.span()) + .unwrap_or_else(Span::call_site); + + let ty = field.and_then(ser_field_expr_type_hint).into_iter(); + match *self { StructTrait::SerializeMap => { - quote!(_serde::ser::SerializeMap::serialize_entry #( ::<_, #ty> )* ) + quote_spanned! {span=> + _serde::ser::SerializeMap::serialize_entry #( ::<_, #ty> )* + } } StructTrait::SerializeStruct => { - quote!(_serde::ser::SerializeStruct::serialize_field #( ::<#ty> )* ) + quote_spanned! {span=> + _serde::ser::SerializeStruct::serialize_field #( ::<#ty> )* + } } StructTrait::SerializeStructVariant => { - quote!(_serde::ser::SerializeStructVariant::serialize_field #( ::<#ty> )* ) + quote_spanned! {span=> + _serde::ser::SerializeStructVariant::serialize_field #( ::<#ty> )* + } } } } @@ -1346,18 +1346,41 @@ enum TupleTrait { } impl TupleTrait { - fn serialize_element(&self, ty: Option<&syn::Type>) -> TokenStream { - let ty = ty.into_iter(); + fn serialize_element(&self, field: &Field) -> TokenStream { + let span = field.original.span(); + let ty = ser_field_expr_type_hint(field).into_iter(); + match *self { TupleTrait::SerializeTuple => { - quote!(_serde::ser::SerializeTuple::serialize_element #( ::<#ty> )*) + quote_spanned! {span=> + _serde::ser::SerializeTuple::serialize_element #( ::<#ty> )* + } } TupleTrait::SerializeTupleStruct => { - quote!(_serde::ser::SerializeTupleStruct::serialize_field #( ::<#ty> )*) + quote_spanned! {span=> + _serde::ser::SerializeTupleStruct::serialize_field #( ::<#ty> )* + } } TupleTrait::SerializeTupleVariant => { - quote!(_serde::ser::SerializeTupleVariant::serialize_field #( ::<#ty> )*) + quote_spanned! {span=> + _serde::ser::SerializeTupleVariant::serialize_field #( ::<#ty> )* + } } } } } + +/// Returns the field's type unless `serialize_with` is enabled. +/// This type hint should be used in a turbofish expression to hint the +/// compiler where the type of the field comes from. This way an error +/// that "field's type doesn't implement Serialize" will point to the +/// type type of the field directly. +fn ser_field_expr_type_hint<'a>(field: &Field<'a>) -> Option<&'a syn::Type> { + // If `serialize_with` is enabled the type of the serialized field + // expression will not correlate with the type of the field. + if field.attrs.serialize_with().is_some() { + return None; + } + + Some(field.ty) +} From 02b2f86476779db3ca6c451018385f6443a732f5 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Mon, 21 Oct 2024 22:34:08 +0000 Subject: [PATCH 3/5] Move span variable closer to usage --- serde_derive/src/ser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serde_derive/src/ser.rs b/serde_derive/src/ser.rs index c8eddd70e..2dab65558 100644 --- a/serde_derive/src/ser.rs +++ b/serde_derive/src/ser.rs @@ -1105,7 +1105,6 @@ fn serialize_struct_visitor( .filter(|&field| !field.attrs.skip_serializing()) .map(|field| { let member = &field.member; - let span = field.original.span(); let mut field_expr = if is_enum { quote!(#member) @@ -1125,6 +1124,7 @@ fn serialize_struct_visitor( } let ser = if field.attrs.flatten() { + let span = field.original.span(); let func = quote_spanned!(span=> _serde::Serialize::serialize); quote! { #func(&#field_expr, _serde::__private::ser::FlatMapSerializer(&mut __serde_state))?; From cecb90af58ef2a4a20a8f91c036936bee7452c5d Mon Sep 17 00:00:00 2001 From: Veetaha Date: Mon, 21 Oct 2024 22:35:26 +0000 Subject: [PATCH 4/5] Fix grammar --- serde_derive/src/ser.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/serde_derive/src/ser.rs b/serde_derive/src/ser.rs index 2dab65558..af0e90716 100644 --- a/serde_derive/src/ser.rs +++ b/serde_derive/src/ser.rs @@ -1373,8 +1373,8 @@ impl TupleTrait { /// Returns the field's type unless `serialize_with` is enabled. /// This type hint should be used in a turbofish expression to hint the /// compiler where the type of the field comes from. This way an error -/// that "field's type doesn't implement Serialize" will point to the -/// type type of the field directly. +/// that "field's type doesn't implement Serialize" will point to the type +/// of the field directly. fn ser_field_expr_type_hint<'a>(field: &Field<'a>) -> Option<&'a syn::Type> { // If `serialize_with` is enabled the type of the serialized field // expression will not correlate with the type of the field. From 5a445e668e479e7f31f68a71328cc42855996a98 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Mon, 21 Oct 2024 22:36:57 +0000 Subject: [PATCH 5/5] Fix span variable location --- serde_derive/src/ser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serde_derive/src/ser.rs b/serde_derive/src/ser.rs index af0e90716..941bef638 100644 --- a/serde_derive/src/ser.rs +++ b/serde_derive/src/ser.rs @@ -1123,8 +1123,8 @@ fn serialize_struct_visitor( field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr); } + let span = field.original.span(); let ser = if field.attrs.flatten() { - let span = field.original.span(); let func = quote_spanned!(span=> _serde::Serialize::serialize); quote! { #func(&#field_expr, _serde::__private::ser::FlatMapSerializer(&mut __serde_state))?;