aboutsummaryrefslogtreecommitdiff
path: root/vendor/serde_derive/src/internals/check.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/serde_derive/src/internals/check.rs')
-rw-r--r--vendor/serde_derive/src/internals/check.rs477
1 files changed, 0 insertions, 477 deletions
diff --git a/vendor/serde_derive/src/internals/check.rs b/vendor/serde_derive/src/internals/check.rs
deleted file mode 100644
index 52b0f37..0000000
--- a/vendor/serde_derive/src/internals/check.rs
+++ /dev/null
@@ -1,477 +0,0 @@
-use crate::internals::ast::{Container, Data, Field, Style};
-use crate::internals::attr::{Default, Identifier, TagType};
-use crate::internals::{ungroup, 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: &mut Container, derive: Derive) {
- check_default_on_tuple(cx, cont);
- check_remote_generic(cx, cont);
- 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, derive);
- check_from_and_try_from(cx, cont);
-}
-
-// If some field of a tuple struct is marked #[serde(default)] then all fields
-// after it must also be marked with that attribute, or the struct must have a
-// container-level serde(default) attribute. A field's default value is only
-// used for tuple fields if the sequence is exhausted at that point; that means
-// all subsequent fields will fail to deserialize if they don't have their own
-// default.
-fn check_default_on_tuple(cx: &Ctxt, cont: &Container) {
- if let Default::None = cont.attrs.default() {
- if let Data::Struct(Style::Tuple, fields) = &cont.data {
- let mut first_default_index = None;
- for (i, field) in fields.iter().enumerate() {
- // Skipped fields automatically get the #[serde(default)]
- // attribute. We are interested only on non-skipped fields here.
- if field.attrs.skip_deserializing() {
- continue;
- }
- if let Default::None = field.attrs.default() {
- if let Some(first) = first_default_index {
- cx.error_spanned_by(
- field.ty,
- format!("field must have #[serde(default)] because previous field {} has #[serde(default)]", first),
- );
- }
- continue;
- }
- if first_default_index.is_none() {
- first_default_index = Some(i);
- }
- }
- }
- }
-}
-
-// Remote derive definition type must have either all of the generics of the
-// remote type:
-//
-// #[serde(remote = "Generic")]
-// struct Generic<T> {…}
-//
-// or none of them, i.e. defining impls for one concrete instantiation of the
-// remote type only:
-//
-// #[serde(remote = "Generic<T>")]
-// struct ConcreteDef {…}
-//
-fn check_remote_generic(cx: &Ctxt, cont: &Container) {
- if let Some(remote) = cont.attrs.remote() {
- let local_has_generic = !cont.generics.params.is_empty();
- let remote_has_generic = !remote.segments.last().unwrap().arguments.is_none();
- if local_has_generic && remote_has_generic {
- cx.error_spanned_by(remote, "remove generic parameters from this path");
- }
- }
-}
-
-// Getters are only allowed inside structs (not enums) with the `remote`
-// attribute.
-fn check_getter(cx: &Ctxt, cont: &Container) {
- match cont.data {
- Data::Enum(_) => {
- if cont.data.has_getter() {
- cx.error_spanned_by(
- cont.original,
- "#[serde(getter = \"...\")] is not allowed in an enum",
- );
- }
- }
- Data::Struct(_, _) => {
- if cont.data.has_getter() && cont.attrs.remote().is_none() {
- cx.error_spanned_by(
- cont.original,
- "#[serde(getter = \"...\")] can only be used in structs that have #[serde(remote = \"...\")]",
- );
- }
- }
- }
-}
-
-// Flattening has some restrictions we can test.
-fn check_flatten(cx: &Ctxt, cont: &Container) {
- match &cont.data {
- Data::Enum(variants) => {
- for variant in variants {
- for field in &variant.fields {
- check_flatten_field(cx, variant.style, field);
- }
- }
- }
- Data::Struct(style, fields) => {
- for field in fields {
- check_flatten_field(cx, *style, field);
- }
- }
- }
-}
-
-fn check_flatten_field(cx: &Ctxt, style: Style, field: &Field) {
- if !field.attrs.flatten() {
- return;
- }
- match style {
- Style::Tuple => {
- cx.error_spanned_by(
- field.original,
- "#[serde(flatten)] cannot be used on tuple structs",
- );
- }
- Style::Newtype => {
- cx.error_spanned_by(
- field.original,
- "#[serde(flatten)] cannot be used on newtype structs",
- );
- }
- _ => {}
- }
-}
-
-// The `other` attribute must be used at most once and it must be the last
-// variant of an enum.
-//
-// Inside a `variant_identifier` all variants must be unit variants. Inside a
-// `field_identifier` all but possibly one variant must be unit variants. The
-// last variant may be a newtype variant which is an implicit "other" case.
-fn check_identifier(cx: &Ctxt, cont: &Container) {
- let variants = match &cont.data {
- Data::Enum(variants) => variants,
- Data::Struct(_, _) => return,
- };
-
- for (i, variant) in variants.iter().enumerate() {
- match (
- variant.style,
- cont.attrs.identifier(),
- variant.attrs.other(),
- cont.attrs.tag(),
- ) {
- // The `other` attribute may not be used in a variant_identifier.
- (_, Identifier::Variant, true, _) => {
- cx.error_spanned_by(
- variant.original,
- "#[serde(other)] may not be used on a variant identifier",
- );
- }
-
- // Variant with `other` attribute cannot appear in untagged enum
- (_, Identifier::No, true, &TagType::None) => {
- cx.error_spanned_by(
- variant.original,
- "#[serde(other)] cannot appear on untagged enum",
- );
- }
-
- // Variant with `other` attribute must be the last one.
- (Style::Unit, Identifier::Field, true, _) | (Style::Unit, Identifier::No, true, _) => {
- if i < variants.len() - 1 {
- cx.error_spanned_by(
- variant.original,
- "#[serde(other)] must be on the last variant",
- );
- }
- }
-
- // Variant with `other` attribute must be a unit variant.
- (_, Identifier::Field, true, _) | (_, Identifier::No, true, _) => {
- cx.error_spanned_by(
- variant.original,
- "#[serde(other)] must be on a unit variant",
- );
- }
-
- // Any sort of variant is allowed if this is not an identifier.
- (_, Identifier::No, false, _) => {}
-
- // Unit variant without `other` attribute is always fine.
- (Style::Unit, _, false, _) => {}
-
- // The last field is allowed to be a newtype catch-all.
- (Style::Newtype, Identifier::Field, false, _) => {
- if i < variants.len() - 1 {
- cx.error_spanned_by(
- variant.original,
- format!("`{}` must be the last variant", variant.ident),
- );
- }
- }
-
- (_, Identifier::Field, false, _) => {
- cx.error_spanned_by(
- variant.original,
- "#[serde(field_identifier)] may only contain unit variants",
- );
- }
-
- (_, Identifier::Variant, false, _) => {
- cx.error_spanned_by(
- variant.original,
- "#[serde(variant_identifier)] may only contain unit variants",
- );
- }
- }
- }
-}
-
-// Skip-(de)serializing attributes are not allowed on variants marked
-// (de)serialize_with.
-fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) {
- let variants = match &cont.data {
- Data::Enum(variants) => variants,
- Data::Struct(_, _) => return,
- };
-
- for variant in variants {
- if variant.attrs.serialize_with().is_some() {
- if variant.attrs.skip_serializing() {
- cx.error_spanned_by(
- variant.original,
- format!(
- "variant `{}` cannot have both #[serde(serialize_with)] and #[serde(skip_serializing)]",
- variant.ident
- ),
- );
- }
-
- for field in &variant.fields {
- let member = member_message(&field.member);
-
- if field.attrs.skip_serializing() {
- cx.error_spanned_by(
- variant.original,
- format!(
- "variant `{}` cannot have both #[serde(serialize_with)] and a field {} marked with #[serde(skip_serializing)]",
- variant.ident, member
- ),
- );
- }
-
- if field.attrs.skip_serializing_if().is_some() {
- cx.error_spanned_by(
- variant.original,
- format!(
- "variant `{}` cannot have both #[serde(serialize_with)] and a field {} marked with #[serde(skip_serializing_if)]",
- variant.ident, member
- ),
- );
- }
- }
- }
-
- if variant.attrs.deserialize_with().is_some() {
- if variant.attrs.skip_deserializing() {
- cx.error_spanned_by(
- variant.original,
- format!(
- "variant `{}` cannot have both #[serde(deserialize_with)] and #[serde(skip_deserializing)]",
- variant.ident
- ),
- );
- }
-
- for field in &variant.fields {
- if field.attrs.skip_deserializing() {
- let member = member_message(&field.member);
-
- cx.error_spanned_by(
- variant.original,
- format!(
- "variant `{}` cannot have both #[serde(deserialize_with)] and a field {} marked with #[serde(skip_deserializing)]",
- variant.ident, member
- ),
- );
- }
- }
- }
- }
-}
-
-// The tag of an internally-tagged struct variant must not be the same as either
-// one of its fields, as this would result in duplicate keys in the serialized
-// output and/or ambiguity in the to-be-deserialized input.
-fn check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container) {
- let variants = match &cont.data {
- Data::Enum(variants) => variants,
- Data::Struct(_, _) => return,
- };
-
- let tag = match cont.attrs.tag() {
- TagType::Internal { tag } => tag.as_str(),
- TagType::External | TagType::Adjacent { .. } | TagType::None => return,
- };
-
- let diagnose_conflict = || {
- cx.error_spanned_by(
- cont.original,
- format!("variant field name `{}` conflicts with internal tag", tag),
- );
- };
-
- for variant in variants {
- match variant.style {
- Style::Struct => {
- if variant.attrs.untagged() {
- continue;
- }
- for field in &variant.fields {
- let check_ser =
- !(field.attrs.skip_serializing() || variant.attrs.skip_serializing());
- let check_de =
- !(field.attrs.skip_deserializing() || variant.attrs.skip_deserializing());
- let name = field.attrs.name();
- let ser_name = name.serialize_name();
-
- if check_ser && ser_name == tag {
- diagnose_conflict();
- return;
- }
-
- for de_name in field.attrs.aliases() {
- if check_de && de_name == tag {
- diagnose_conflict();
- return;
- }
- }
- }
- }
- Style::Unit | Style::Newtype | Style::Tuple => {}
- }
- }
-}
-
-// In the case of adjacently-tagged enums, the type and the contents tag must
-// differ, for the same reason.
-fn check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container) {
- let (type_tag, content_tag) = match cont.attrs.tag() {
- TagType::Adjacent { tag, content } => (tag, content),
- TagType::Internal { .. } | TagType::External | TagType::None => return,
- };
-
- if type_tag == content_tag {
- cx.error_spanned_by(
- cont.original,
- format!(
- "enum tags `{}` for type and content conflict with each other",
- type_tag
- ),
- );
- }
-}
-
-// Enums and unit structs cannot be transparent.
-fn check_transparent(cx: &Ctxt, cont: &mut Container, derive: Derive) {
- if !cont.attrs.transparent() {
- return;
- }
-
- if cont.attrs.type_from().is_some() {
- cx.error_spanned_by(
- cont.original,
- "#[serde(transparent)] is not allowed with #[serde(from = \"...\")]",
- );
- }
-
- if cont.attrs.type_try_from().is_some() {
- cx.error_spanned_by(
- cont.original,
- "#[serde(transparent)] is not allowed with #[serde(try_from = \"...\")]",
- );
- }
-
- if cont.attrs.type_into().is_some() {
- cx.error_spanned_by(
- cont.original,
- "#[serde(transparent)] is not allowed with #[serde(into = \"...\")]",
- );
- }
-
- let fields = match &mut cont.data {
- Data::Enum(_) => {
- cx.error_spanned_by(
- cont.original,
- "#[serde(transparent)] is not allowed on an enum",
- );
- return;
- }
- Data::Struct(Style::Unit, _) => {
- cx.error_spanned_by(
- cont.original,
- "#[serde(transparent)] is not allowed on a unit struct",
- );
- return;
- }
- Data::Struct(_, fields) => fields,
- };
-
- let mut transparent_field = None;
-
- for field in fields {
- if allow_transparent(field, derive) {
- if transparent_field.is_some() {
- cx.error_spanned_by(
- cont.original,
- "#[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_spanned_by(
- cont.original,
- "#[serde(transparent)] requires at least one field that is not skipped",
- );
- }
- Derive::Deserialize => {
- cx.error_spanned_by(
- cont.original,
- "#[serde(transparent)] requires at least one field that is neither skipped nor has a default",
- );
- }
- },
- }
-}
-
-fn member_message(member: &Member) -> String {
- match member {
- Member::Named(ident) => format!("`{}`", ident),
- Member::Unnamed(i) => format!("#{}", i.index),
- }
-}
-
-fn allow_transparent(field: &Field, derive: Derive) -> bool {
- if let Type::Path(ty) = ungroup(field.ty) {
- if let Some(seg) = ty.path.segments.last() {
- if seg.ident == "PhantomData" {
- return false;
- }
- }
- }
-
- match derive {
- Derive::Serialize => !field.attrs.skip_serializing(),
- Derive::Deserialize => !field.attrs.skip_deserializing() && field.attrs.default().is_none(),
- }
-}
-
-fn check_from_and_try_from(cx: &Ctxt, cont: &mut Container) {
- if cont.attrs.type_from().is_some() && cont.attrs.type_try_from().is_some() {
- cx.error_spanned_by(
- cont.original,
- "#[serde(from = \"...\")] and #[serde(try_from = \"...\")] conflict with each other",
- );
- }
-}