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

Empty adjacently-tagged enum generates broken code #785

Closed
dtolnay opened this issue Feb 21, 2017 · 4 comments
Closed

Empty adjacently-tagged enum generates broken code #785

dtolnay opened this issue Feb 21, 2017 · 4 comments
Assignees

Comments

@dtolnay
Copy link
Member

dtolnay commented Feb 21, 2017

#[derive(Deserialize)]
#[serde(tag = "t", content = "c")]
enum Void {
}
The generated code looks like this (click to expand)
impl Deserialize for Void {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where D: Deserializer
    {
        enum Field {}
        impl Deserialize for Field {
            #[inline]
            fn deserialize<D>(deserializer: D)
                                -> Result<Field, D::Error>
                where D: Deserializer
            {
                struct FieldVisitor;
                impl Visitor for FieldVisitor {
                    type Value = Field;
                    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                        fmt::Formatter::write_str(formatter, "field name")
                    }
                    fn visit_u32<E>(self, value: u32) -> Result<Field, E>
                        where E: de::Error
                    {
                        match value {
                            _ => Err(de::Error::invalid_value(
                                        de::Unexpected::Unsigned(value as u64),
                                        &"variant index 0 <= i < 0")),
                        }
                    }
                    fn visit_str<E>(self, value: &str) -> Result<Field, E>
                        where E: de::Error
                    {
                        match value {
                            _ => Err(de::Error::unknown_variant(value, VARIANTS)),
                        }
                    }
                    fn visit_bytes<E>(self, value: &[u8]) -> Result<Field, E>
                        where E: de::Error
                    {
                        match value {
                            _ => {
                                let value = &serde::export::from_utf8_lossy(value);
                                Err(de::Error::unknown_variant(value, VARIANTS))
                            }
                        }
                    }
                }
                Deserializer::deserialize_struct_field(deserializer, FieldVisitor)
            }
        }
        const VARIANTS: &'static [&'static str] = &[];
        struct Seed {
            field: Field,
            marker: PhantomData<Void>,
        }
        impl de::DeserializeSeed for Seed {
            type Value = Void;
            fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
                where D: Deserializer
            {
                match self.field {}
            }
        }
        struct VoidVisitor {
            marker: PhantomData<Void>,
        }
        impl Visitor for VoidVisitor {
            type Value = Void;
            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                fmt::Formatter::write_str(formatter, "adjacently tagged enum Void")
            }
            fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
                where V: MapVisitor
            {
                match MapVisitor::visit_key_seed(&mut visitor, de::private::TagOrContentFieldVisitor{tag: "t", content: "c"})? {
                    Some(de::private::TagOrContentField::Tag) => {
                        let field = MapVisitor::visit_value(&mut visitor)?;
                        match MapVisitor::visit_key_seed(&mut visitor, de::private::TagOrContentFieldVisitor{tag: "t", content: "c"})? {
                            Some(de::private::TagOrContentField::Tag) => {
                                Err(de::Error::duplicate_field("t"))
                            }
                            Some(de::private::TagOrContentField::Content) => {
                                let ret = MapVisitor::visit_value_seed(&mut visitor, Seed{field: field, marker: PhantomData})?;
                                match MapVisitor::visit_key_seed(&mut visitor, de::private::TagOrContentFieldVisitor{tag: "t", content: "c"})? {
                                    Some(de::private::TagOrContentField::Tag) => {
                                        Err(de::Error::duplicate_field("t"))
                                    }
                                    Some(de::private::TagOrContentField::Content) => {
                                        Err(de::Error::duplicate_field("c"))
                                    }
                                    None => Ok(ret),
                                }
                            }
                            None => Err(de::Error::missing_field("c")),
                        }
                    }
                    Some(de::private::TagOrContentField::Content) => {
                        let content = MapVisitor::visit_value::<de::private::Content>(&mut visitor)?;
                        match MapVisitor::visit_key_seed(&mut visitor, de::private::TagOrContentFieldVisitor{tag: "t", content: "c"})? {
                            Some(de::private::TagOrContentField::Tag) => {
                                let deserializer = de::private::ContentDeserializer::<V::Error>::new(content);
                                let ret =
                                    match match match de::MapVisitor::visit_value(&mut visitor) {
                                        Ok(val) => val,
                                        Err(err) => return Err(From::from(err)),
                                    } {} {
                                        Ok(val) => val,
                                        Err(err) => return Err(From::from(err)),
                                    };
                                match MapVisitor::visit_key_seed(&mut visitor, de::private::TagOrContentFieldVisitor{tag: "t", content: "c"})? {
                                    Some(de::private::TagOrContentField::Tag) => {
                                        Err(de::Error::duplicate_field("t"))
                                    }
                                    Some(de::private::TagOrContentField::Content) => {
                                        Err(de::Error::duplicate_field("c"))
                                    }
                                    None => Ok(ret),
                                }
                            }
                            Some(de::private::TagOrContentField::Content) => {
                                Err(de::Error::duplicate_field("c"))
                            }
                            None => Err(de::Error::missing_field("t")),
                        }
                    }
                    None => Err(de::Error::missing_field("t")),
                }
            }
            fn visit_seq<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
                where V: SeqVisitor
            {
                match SeqVisitor::visit(&mut visitor)? {
                    Some(field) => {
                        match SeqVisitor::visit_seed(&mut visitor, Seed{field: field, marker: PhantomData})? {
                            Some(ret) => Ok(ret),
                            None => Err(de::Error::invalid_length(1, &self)),
                        }
                    }
                    None => Err(de::Error::invalid_length(0, &self)),
                }
            }
        }
        const FIELDS: &'static [&'static str] = &["t", "c"];
        Deserializer::deserialize_struct(deserializer, "Void", FIELDS,
                                         VoidVisitor {
                                             marker: PhantomData::<Void>,
                                         })
    }
}

Specifically, the problem is:

let ret =
    match match match de::MapVisitor::visit_value(&mut visitor) {
        Ok(val) => val,
        Err(err) => return Err(From::from(err)),
    } {} {
        Ok(val) => val,
        Err(err) => return Err(From::from(err)),
    };

We probably just want to special-case the case of an enum with no deserialized variants, and generate a one-line impl that unconditionally returns error.

@dtolnay
Copy link
Member Author

dtolnay commented Feb 21, 2017

@elliottslaughter would you be able to give this a shot?

@elliottslaughter
Copy link
Contributor

I can do this, but I'm wondering why you wouldn't just throw an error at compile time instead. Is it because you expect these enums to also be produced by a code-generator in some cases? Because if this were user-written code, it would seem non-sensical to ask to (de)serialize a value that can't itself be instantiated.

@dtolnay
Copy link
Member Author

dtolnay commented Feb 22, 2017

It isn't necessarily nonsensical in handwritten code. One example would be byteorder::BigEndian - see BurntSushi/byteorder#52 for the justification. Another case is cfg'd variants - concrete example - where you want the code to compile even with no features.

@dtolnay dtolnay self-assigned this May 7, 2018
@dtolnay
Copy link
Member Author

dtolnay commented May 8, 2018

Fixed in 12fe42e.

@dtolnay dtolnay closed this as completed May 8, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

2 participants