Skip to content

Commit d3924de

Browse files
bors[bot]Bogay
andauthored
Merge #962
962: allow deriving of `FromVariant` for uninhabitable enums r=Bromeon a=Bogay Fixs #444 What type of error should be returned might need further discussion. Co-authored-by: bogay <[email protected]>
2 parents 4e86808 + 94d7a45 commit d3924de

File tree

3 files changed

+76
-46
lines changed

3 files changed

+76
-46
lines changed

gdnative-derive/src/variant/from.rs

Lines changed: 48 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,6 @@ pub(crate) fn expand_from_variant(derive_data: DeriveData) -> Result<TokenStream
3131
}
3232
}
3333
Repr::Enum(variants) => {
34-
if variants.is_empty() {
35-
return Err(syn::Error::new(
36-
ident.span(),
37-
"cannot derive FromVariant for an uninhabited enum",
38-
));
39-
}
40-
4134
let var_input_ident = Ident::new("__enum_variant", Span::call_site());
4235

4336
let var_ident_strings: Vec<String> = variants
@@ -62,46 +55,55 @@ pub(crate) fn expand_from_variant(derive_data: DeriveData) -> Result<TokenStream
6255

6356
let var_input_ident_iter = std::iter::repeat(&var_input_ident);
6457

58+
// Return `FromVariantError` if input is an uninhabitable enum
59+
let early_return = variants.is_empty().then(|| {
60+
quote! {
61+
return Err(FVE::UnknownEnumVariant {
62+
variant: __key,
63+
expected: &[],
64+
});
65+
}
66+
});
67+
6568
quote! {
66-
{
67-
let __dict = ::gdnative::core_types::Dictionary::from_variant(#input_ident)
68-
.map_err(|__err| FVE::InvalidEnumRepr {
69-
expected: VariantEnumRepr::ExternallyTagged,
70-
error: std::boxed::Box::new(__err),
71-
})?;
72-
73-
let __keys = __dict.keys();
74-
if __keys.len() != 1 {
75-
Err(FVE::InvalidEnumRepr {
76-
expected: VariantEnumRepr::ExternallyTagged,
77-
error: std::boxed::Box::new(FVE::InvalidLength {
78-
expected: 1,
79-
len: __keys.len() as usize,
80-
}),
81-
})
82-
}
83-
else {
84-
let __key = String::from_variant(&__keys.get(0))
85-
.map_err(|__err| FVE::InvalidEnumRepr {
86-
expected: VariantEnumRepr::ExternallyTagged,
87-
error: std::boxed::Box::new(__err),
88-
})?;
89-
match __key.as_str() {
90-
#(
91-
#ref_var_ident_string_literals => {
92-
let #var_input_ident_iter = &__dict.get_or_nil(&__keys.get(0));
93-
(#var_from_variants).map_err(|err| FVE::InvalidEnumVariant {
94-
variant: #ref_var_ident_string_literals,
95-
error: std::boxed::Box::new(err),
96-
})
97-
},
98-
)*
99-
variant => Err(FVE::UnknownEnumVariant {
100-
variant: variant.to_string(),
101-
expected: &[#(#ref_var_ident_string_literals),*],
102-
}),
103-
}
104-
}
69+
let __dict = ::gdnative::core_types::Dictionary::from_variant(#input_ident)
70+
.map_err(|__err| FVE::InvalidEnumRepr {
71+
expected: VariantEnumRepr::ExternallyTagged,
72+
error: std::boxed::Box::new(__err),
73+
})?;
74+
let __keys = __dict.keys();
75+
if __keys.len() != 1 {
76+
return Err(FVE::InvalidEnumRepr {
77+
expected: VariantEnumRepr::ExternallyTagged,
78+
error: std::boxed::Box::new(FVE::InvalidLength {
79+
expected: 1,
80+
len: __keys.len() as usize,
81+
}),
82+
})
83+
}
84+
85+
let __key = String::from_variant(&__keys.get(0))
86+
.map_err(|__err| FVE::InvalidEnumRepr {
87+
expected: VariantEnumRepr::ExternallyTagged,
88+
error: std::boxed::Box::new(__err),
89+
})?;
90+
91+
#early_return
92+
93+
match __key.as_str() {
94+
#(
95+
#ref_var_ident_string_literals => {
96+
let #var_input_ident_iter = &__dict.get_or_nil(&__keys.get(0));
97+
(#var_from_variants).map_err(|err| FVE::InvalidEnumVariant {
98+
variant: #ref_var_ident_string_literals,
99+
error: std::boxed::Box::new(err),
100+
})
101+
},
102+
)*
103+
variant => Err(FVE::UnknownEnumVariant {
104+
variant: variant.to_string(),
105+
expected: &[#(#ref_var_ident_string_literals),*],
106+
}),
105107
}
106108
}
107109
}

gdnative-derive/src/variant/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,17 @@ pub(crate) fn derive_from_variant(derive_input: DeriveInput) -> Result<TokenStre
107107
let variant = parse_derive_input(derive_input, &bound, Direction::From);
108108
from::expand_from_variant(variant?)
109109
}
110+
111+
#[cfg(test)]
112+
mod tests {
113+
use super::*;
114+
115+
#[test]
116+
fn derive_from_variant_on_uninhabitable_enums() {
117+
let input = parse_quote! {
118+
enum Test {}
119+
};
120+
121+
derive_from_variant(input).unwrap();
122+
}
123+
}

test/src/test_derive.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::cell::{self, Cell, RefCell};
2+
use std::collections::HashMap;
23
use std::rc::Rc;
34

45
use gdnative::export::Property;
@@ -146,6 +147,19 @@ crate::godot_itest! { test_derive_to_variant {
146147
Ok(ToVarTuple::<f64, i128>(1, 0, false)),
147148
ToVarTuple::from_variant(&variant)
148149
);
150+
151+
// Derive on uninhabitable enum results an error
152+
#[derive(Debug, PartialEq, FromVariant)]
153+
enum NoVariant {}
154+
155+
let input = HashMap::from_iter([("foo", "bar")]).to_variant();
156+
assert_eq!(
157+
NoVariant::from_variant(&input),
158+
Err(FromVariantError::UnknownEnumVariant {
159+
variant: "foo".into(),
160+
expected: &[]
161+
})
162+
);
149163
}}
150164

151165
// ----------------------------------------------------------------------------------------------------------------------------------------------

0 commit comments

Comments
 (0)