Improve error messages related to transparent

This commit is contained in:
David Tolnay
2018-05-20 15:09:51 -07:00
parent 1335f85213
commit ac1b25e91d
6 changed files with 74 additions and 71 deletions
+48 -7
View File
@@ -8,19 +8,19 @@
use internals::ast::{Container, Data, Field, Style};
use internals::attr::{EnumTag, Identifier};
use internals::Ctxt;
use syn::Member;
use internals::{Ctxt, Derive};
use syn::{Member, Type};
/// Cross-cutting checks that require looking at more than a single attrs
/// object. Simpler checks should happen when parsing and building the attrs.
pub fn check(cx: &Ctxt, cont: &Container) {
pub fn check(cx: &Ctxt, cont: &mut Container, derive: Derive) {
check_getter(cx, cont);
check_flatten(cx, cont);
check_identifier(cx, cont);
check_variant_skip_attrs(cx, cont);
check_internal_tag_field_name_conflict(cx, cont);
check_adjacent_tag_conflict(cx, cont);
check_transparent(cx, cont);
check_transparent(cx, cont, derive);
}
/// Getters are only allowed inside structs (not enums) with the `remote`
@@ -280,7 +280,7 @@ fn check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container) {
}
/// Enums and unit structs cannot be transparent.
fn check_transparent(cx: &Ctxt, cont: &Container) {
fn check_transparent(cx: &Ctxt, cont: &mut Container, derive: Derive) {
if !cont.attrs.transparent() {
return;
}
@@ -293,14 +293,40 @@ fn check_transparent(cx: &Ctxt, cont: &Container) {
cx.error("#[serde(transparent)] is not allowed with #[serde(into = \"...\")]");
}
match cont.data {
let fields = match cont.data {
Data::Enum(_) => {
cx.error("#[serde(transparent)] is not allowed on an enum");
return;
}
Data::Struct(Style::Unit, _) => {
cx.error("#[serde(transparent)] is not allowed on a unit struct");
return;
}
Data::Struct(_, ref mut fields) => fields,
};
let mut transparent_field = None;
for field in fields {
if allow_transparent(field, derive) {
if transparent_field.is_some() {
cx.error("#[serde(transparent)] requires struct to have at most one transparent field");
return;
}
transparent_field = Some(field);
}
}
match transparent_field {
Some(transparent_field) => transparent_field.attrs.mark_transparent(),
None => match derive {
Derive::Serialize => {
cx.error("#[serde(transparent)] requires at least one field that is not skipped");
}
Derive::Deserialize => {
cx.error("#[serde(transparent)] requires at least one field that is neither skipped nor has a default");
}
}
_ => {}
}
}
@@ -310,3 +336,18 @@ fn member_message(member: &Member) -> String {
Member::Unnamed(ref i) => i.index.to_string(),
}
}
fn allow_transparent(field: &Field, derive: Derive) -> bool {
if let Type::Path(ref ty) = *field.ty {
if let Some(seg) = ty.path.segments.last() {
if seg.into_value().ident == "PhantomData" {
return false;
}
}
}
match derive {
Derive::Serialize => !field.attrs.skip_serializing(),
Derive::Deserialize => !field.attrs.skip_deserializing() && field.attrs.default().is_none(),
}
}