From a799ea171cace988c7ae9856b5b23bd02a2142b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?A=CC=81rpa=CC=81d=20Goretity?= Date: Thu, 8 Mar 2018 00:43:35 +0100 Subject: [PATCH] Disallow variant field names to conflict with tag of internally-tagged enum --- serde_derive_internals/src/check.rs | 46 ++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/serde_derive_internals/src/check.rs b/serde_derive_internals/src/check.rs index 0d6946ff..0f5c17f7 100644 --- a/serde_derive_internals/src/check.rs +++ b/serde_derive_internals/src/check.rs @@ -7,7 +7,7 @@ // except according to those terms. use ast::{Data, Container, Style}; -use attr::Identifier; +use attr::{Identifier, EnumTag}; use Ctxt; /// Cross-cutting checks that require looking at more than a single attrs @@ -16,6 +16,7 @@ pub fn check(cx: &Ctxt, cont: &Container) { check_getter(cx, cont); check_identifier(cx, cont); check_variant_skip_attrs(cx, cont); + check_internally_tagged_variant_name_conflict(cx, cont); } /// Getters are only allowed inside structs (not enums) with the `remote` @@ -169,3 +170,46 @@ fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) { } } } + +fn check_internally_tagged_variant_name_conflict( + cx: &Ctxt, + cont: &Container, +) { + let variants = match cont.data { + Data::Enum(ref variants) => variants, + Data::Struct(_, _) => return, + }; + + let tag = match *cont.attrs.tag() { + EnumTag::Internal { ref tag } => tag.as_str(), + EnumTag::External | EnumTag::Adjacent { .. } | EnumTag::None => return, + }; + + let diagnose_conflict = || { + let message = format!( + "variant field name `{}` conflicts with internal tag", + tag + ); + cx.error(message); + }; + + for variant in variants { + match variant.style { + Style::Struct => { + for field in &variant.fields { + let check_ser = !field.attrs.skip_serializing(); + let check_de = !field.attrs.skip_deserializing(); + let name = field.attrs.name(); + let ser_name = name.serialize_name(); + let de_name = name.deserialize_name(); + + if check_ser && ser_name == tag || check_de && de_name == tag { + diagnose_conflict(); + return; + } + } + }, + _ => {}, + } + } +}