Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make compile error message point to field type instead of call site for "not implemented Serialize trait" #2837

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 48 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,7 @@ 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 func = tuple_trait.serialize_element(field);
let ser = quote! {
#func(&mut __serde_state, #field_expr)?;
};
Expand Down Expand Up @@ -1131,7 +1130,8 @@ fn serialize_struct_visitor(
#func(&#field_expr, _serde::__private::ser::FlatMapSerializer(&mut __serde_state))?;
}
} else {
let func = struct_trait.serialize_field(span);
let func = struct_trait.serialize_field(Some(field));

quote! {
#func(&mut __serde_state, #key_expr, #field_expr)?;
}
Expand Down Expand Up @@ -1300,16 +1300,28 @@ enum StructTrait {
}

impl StructTrait {
fn serialize_field(&self, span: Span) -> TokenStream {
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_spanned!(span=> _serde::ser::SerializeMap::serialize_entry)
quote_spanned! {span=>
_serde::ser::SerializeMap::serialize_entry #( ::<_, #ty> )*
}
}
StructTrait::SerializeStruct => {
quote_spanned!(span=> _serde::ser::SerializeStruct::serialize_field)
quote_spanned! {span=>
_serde::ser::SerializeStruct::serialize_field #( ::<#ty> )*
}
}
StructTrait::SerializeStructVariant => {
quote_spanned!(span=> _serde::ser::SerializeStructVariant::serialize_field)
quote_spanned! {span=>
_serde::ser::SerializeStructVariant::serialize_field #( ::<#ty> )*
}
}
}
}
Expand All @@ -1334,17 +1346,41 @@ enum TupleTrait {
}

impl TupleTrait {
fn serialize_element(&self, span: Span) -> TokenStream {
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_spanned!(span=> _serde::ser::SerializeTuple::serialize_element)
quote_spanned! {span=>
_serde::ser::SerializeTuple::serialize_element #( ::<#ty> )*
}
}
TupleTrait::SerializeTupleStruct => {
quote_spanned!(span=> _serde::ser::SerializeTupleStruct::serialize_field)
quote_spanned! {span=>
_serde::ser::SerializeTupleStruct::serialize_field #( ::<#ty> )*
}
}
TupleTrait::SerializeTupleVariant => {
quote_spanned!(span=> _serde::ser::SerializeTupleVariant::serialize_field)
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
/// 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)
}
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
Loading