Skip to content

Commit

Permalink
Make compile error message point to field type instead of call site f…
Browse files Browse the repository at this point in the history
…or not implemented Serialize trait
  • Loading branch information
Veetaha committed Oct 19, 2024
1 parent 4b3178b commit 9144a88
Show file tree
Hide file tree
Showing 11 changed files with 477 additions and 16 deletions.
37 changes: 25 additions & 12 deletions serde_derive/src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
}
Expand Down Expand Up @@ -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)?;
};
Expand Down Expand Up @@ -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)?;
}
Expand Down Expand Up @@ -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> )* )
}
}
}
Expand All @@ -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> )*)
}
}
}
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -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`
Expand All @@ -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>(_: &T) -> String
| --------- required by a bound in this function
Expand All @@ -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`
Expand All @@ -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
Expand Down
11 changes: 11 additions & 0 deletions test_suite/tests/ui/on-unimplemented/struct_field.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use serde_derive::{Deserialize, Serialize};

struct NoImpls;

#[derive(Serialize, Deserialize)]
struct S {
x1: u32,
x2: NoImpls,
}

fn main() {}
166 changes: 166 additions & 0 deletions test_suite/tests/ui/on-unimplemented/struct_field.stderr
Original file line number Diff line number Diff line change
@@ -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<T>(&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<T>(&mut self) -> Result<Option<T>, 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<V>(&mut self) -> Result<V, Self::Error>
| ---------- 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<V, E>
| ------------- 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<T>(&mut self, seed: T) -> Result<Option<T::Value>, 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<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
| --------------- 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)
10 changes: 10 additions & 0 deletions test_suite/tests/ui/on-unimplemented/struct_variant.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use serde_derive::{Deserialize, Serialize};

struct NoImpls;

#[derive(Serialize, Deserialize)]
enum E {
S { x1: u32, x2: NoImpls },
}

fn main() {}
Loading

0 comments on commit 9144a88

Please sign in to comment.