Add compile fail tests for FromPyObject derives + some fixes.
Fix some error messages and accidental passes.
This commit is contained in:
parent
7781bb78de
commit
a8c5379eff
|
@ -1,6 +1,7 @@
|
|||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{parse_quote, Attribute, DataEnum, DeriveInput, Fields, Ident, Meta, Result};
|
||||
|
||||
/// Describes derivation input of an enum.
|
||||
|
@ -17,8 +18,8 @@ impl<'a> Enum<'a> {
|
|||
/// `Identifier` of the enum.
|
||||
fn new(data_enum: &'a DataEnum, ident: &'a Ident) -> Result<Self> {
|
||||
if data_enum.variants.is_empty() {
|
||||
return Err(syn::Error::new_spanned(
|
||||
&data_enum.variants,
|
||||
return Err(spanned_err(
|
||||
&ident,
|
||||
"Cannot derive FromPyObject for empty enum.",
|
||||
));
|
||||
}
|
||||
|
@ -121,6 +122,12 @@ impl<'a> Container<'a> {
|
|||
attrs: Vec<ContainerAttribute>,
|
||||
is_enum_variant: bool,
|
||||
) -> Result<Self> {
|
||||
if fields.is_empty() {
|
||||
return Err(spanned_err(
|
||||
fields,
|
||||
"Cannot derive FromPyObject for empty structs and variants.",
|
||||
));
|
||||
}
|
||||
let transparent = attrs.iter().any(ContainerAttribute::transparent);
|
||||
if transparent {
|
||||
Self::check_transparent_len(fields)?;
|
||||
|
@ -154,10 +161,11 @@ impl<'a> Container<'a> {
|
|||
ContainerType::Struct(fields)
|
||||
}
|
||||
(Fields::Unit, _) => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
// covered by length check above
|
||||
return Err(spanned_err(
|
||||
&fields,
|
||||
"Cannot derive FromPyObject for Unit structs and variants",
|
||||
))
|
||||
));
|
||||
}
|
||||
};
|
||||
let err_name = attrs
|
||||
|
@ -175,16 +183,30 @@ impl<'a> Container<'a> {
|
|||
Ok(v)
|
||||
}
|
||||
|
||||
fn verify_struct_container_attrs(attrs: &'a [ContainerAttribute]) -> Result<()> {
|
||||
fn verify_struct_container_attrs(
|
||||
attrs: &'a [ContainerAttribute],
|
||||
original: &[Attribute],
|
||||
) -> Result<()> {
|
||||
for attr in attrs {
|
||||
match attr {
|
||||
ContainerAttribute::Transparent => continue,
|
||||
ContainerAttribute::ErrorAnnotation(_) => {
|
||||
let span = original
|
||||
.iter()
|
||||
.map(|a| a.span())
|
||||
.fold(None, |mut acc: Option<Span>, span| {
|
||||
if let Some(all) = acc.as_mut() {
|
||||
all.join(span)
|
||||
} else {
|
||||
Some(span)
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(Span::call_site);
|
||||
return Err(syn::Error::new(
|
||||
Span::call_site(),
|
||||
span,
|
||||
"Annotating error messages for structs is \
|
||||
not supported. Remove the annotation attribute.",
|
||||
))
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -264,7 +286,7 @@ impl<'a> Container<'a> {
|
|||
|
||||
fn check_transparent_len(fields: &Fields) -> Result<()> {
|
||||
if fields.len() != 1 {
|
||||
return Err(syn::Error::new_spanned(
|
||||
return Err(spanned_err(
|
||||
fields,
|
||||
"Transparent structs and variants can only have 1 field",
|
||||
));
|
||||
|
@ -315,16 +337,18 @@ impl ContainerAttribute {
|
|||
if let syn::Lit::Str(s) = &nv.lit {
|
||||
attrs.push(ContainerAttribute::ErrorAnnotation(s.value()))
|
||||
} else {
|
||||
return Err(syn::Error::new_spanned(
|
||||
&nv.lit,
|
||||
"Expected string literal.",
|
||||
));
|
||||
return Err(spanned_err(&nv.lit, "Expected string literal."));
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
other => {
|
||||
return Err(spanned_err(
|
||||
other,
|
||||
"Expected `transparent` or `annotation = \"name\"`",
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(syn::Error::new_spanned(
|
||||
return Err(spanned_err(
|
||||
meta,
|
||||
"Unknown container attribute, expected `transparent` or \
|
||||
`annotation(\"err_name\")`",
|
||||
|
@ -352,8 +376,8 @@ impl FieldAttribute {
|
|||
return Ok(None);
|
||||
}
|
||||
if list.nested.len() > 1 {
|
||||
return Err(syn::Error::new_spanned(
|
||||
list,
|
||||
return Err(spanned_err(
|
||||
list.nested,
|
||||
"Only one of `item`, `attribute` can be provided, possibly with an \
|
||||
additional argument: `item(\"key\")` or `attribute(\"name\").",
|
||||
));
|
||||
|
@ -362,7 +386,7 @@ impl FieldAttribute {
|
|||
let meta = match metaitem {
|
||||
syn::NestedMeta::Meta(meta) => meta,
|
||||
syn::NestedMeta::Lit(lit) => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
return Err(spanned_err(
|
||||
lit,
|
||||
"Expected `attribute` or `item`, not a literal.",
|
||||
))
|
||||
|
@ -374,10 +398,7 @@ impl FieldAttribute {
|
|||
} else if path.is_ident("item") {
|
||||
Ok(Some(FieldAttribute::GetItem(Self::item_arg(meta)?)))
|
||||
} else {
|
||||
Err(syn::Error::new_spanned(
|
||||
meta,
|
||||
"Expected `attribute` or `item`.",
|
||||
))
|
||||
Err(spanned_err(meta, "Expected `attribute` or `item`."))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -387,25 +408,25 @@ impl FieldAttribute {
|
|||
syn::Meta::Path(_) => return Ok(None),
|
||||
Meta::NameValue(nv) => {
|
||||
let err_msg = "Expected a string literal or no argument: `pyo3(attribute(\"name\") or `pyo3(attribute)`";
|
||||
return Err(syn::Error::new_spanned(nv, err_msg));
|
||||
return Err(spanned_err(nv, err_msg));
|
||||
}
|
||||
};
|
||||
if arg_list.nested.len() != 1 {
|
||||
return Err(syn::Error::new_spanned(
|
||||
arg_list,
|
||||
"Expected a single string literal.",
|
||||
));
|
||||
let arg_msg = "Expected a single string literal argument.";
|
||||
if arg_list.nested.is_empty() {
|
||||
return Err(spanned_err(arg_list, arg_msg));
|
||||
} else if arg_list.nested.len() > 1 {
|
||||
return Err(spanned_err(arg_list.nested, arg_msg));
|
||||
}
|
||||
let first = arg_list.nested.first().unwrap();
|
||||
if let syn::NestedMeta::Lit(lit) = first {
|
||||
if let syn::Lit::Str(litstr) = lit {
|
||||
if litstr.value().is_empty() {
|
||||
return Err(spanned_err(litstr, "Attribute name cannot be empty."));
|
||||
}
|
||||
return Ok(Some(parse_quote!(#litstr)));
|
||||
}
|
||||
}
|
||||
Err(syn::Error::new_spanned(
|
||||
first,
|
||||
"Expected a single string literal.",
|
||||
))
|
||||
Err(spanned_err(first, arg_msg))
|
||||
}
|
||||
|
||||
fn item_arg(meta: syn::Meta) -> syn::Result<Option<syn::Lit>> {
|
||||
|
@ -413,26 +434,30 @@ impl FieldAttribute {
|
|||
syn::Meta::List(list) => list,
|
||||
syn::Meta::Path(_) => return Ok(None),
|
||||
Meta::NameValue(nv) => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
return Err(spanned_err(
|
||||
nv,
|
||||
"Expected a literal or no argument: `pyo3(item(\"key\") or `pyo3(item)`",
|
||||
))
|
||||
}
|
||||
};
|
||||
if arg_list.nested.len() != 1 {
|
||||
return Err(syn::Error::new_spanned(
|
||||
arg_list,
|
||||
"Expected a single literal.",
|
||||
));
|
||||
let arg_msg = "Expected a single literal argument.";
|
||||
if arg_list.nested.is_empty() {
|
||||
return Err(spanned_err(arg_list, arg_msg));
|
||||
} else if arg_list.nested.len() > 1 {
|
||||
return Err(spanned_err(arg_list.nested, arg_msg));
|
||||
}
|
||||
let first = arg_list.nested.first().unwrap();
|
||||
if let syn::NestedMeta::Lit(lit) = first {
|
||||
return Ok(Some(parse_quote!(#lit)));
|
||||
}
|
||||
Err(syn::Error::new_spanned(first, "Expected a literal."))
|
||||
Err(spanned_err(first, arg_msg))
|
||||
}
|
||||
}
|
||||
|
||||
fn spanned_err<T: ToTokens>(tokens: T, msg: &str) -> syn::Error {
|
||||
syn::Error::new_spanned(tokens, msg)
|
||||
}
|
||||
|
||||
/// Extract pyo3 metalist, flattens multiple lists into a single one.
|
||||
fn get_pyo3_meta_list(attrs: &[Attribute]) -> Result<syn::MetaList> {
|
||||
let mut list: Punctuated<syn::NestedMeta, syn::Token![,]> = Punctuated::new();
|
||||
|
@ -443,12 +468,7 @@ fn get_pyo3_meta_list(attrs: &[Attribute]) -> Result<syn::MetaList> {
|
|||
list.push(meta);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
value,
|
||||
"Expected `pyo3()` attribute.",
|
||||
))
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
Ok(syn::MetaList {
|
||||
|
@ -461,9 +481,9 @@ fn get_pyo3_meta_list(attrs: &[Attribute]) -> Result<syn::MetaList> {
|
|||
fn verify_and_get_lifetime(generics: &syn::Generics) -> Result<Option<&syn::LifetimeDef>> {
|
||||
let lifetimes = generics.lifetimes().collect::<Vec<_>>();
|
||||
if lifetimes.len() > 1 {
|
||||
return Err(syn::Error::new_spanned(
|
||||
return Err(spanned_err(
|
||||
&generics,
|
||||
"FromPyObject can only be derived with at most one lifetime parameter.",
|
||||
"FromPyObject can be derived with at most one lifetime parameter.",
|
||||
));
|
||||
}
|
||||
Ok(lifetimes.into_iter().next())
|
||||
|
@ -500,15 +520,15 @@ pub fn build_derive_from_pyobject(tokens: &DeriveInput) -> Result<TokenStream> {
|
|||
}
|
||||
syn::Data::Struct(st) => {
|
||||
let attrs = ContainerAttribute::parse_attrs(&tokens.attrs)?;
|
||||
Container::verify_struct_container_attrs(&attrs)?;
|
||||
Container::verify_struct_container_attrs(&attrs, &tokens.attrs)?;
|
||||
let ident = &tokens.ident;
|
||||
let st = Container::new(&st.fields, parse_quote!(#ident), attrs, false)?;
|
||||
st.build()
|
||||
}
|
||||
_ => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
syn::Data::Union(_) => {
|
||||
return Err(spanned_err(
|
||||
tokens,
|
||||
"FromPyObject can only be derived for structs and enums.",
|
||||
"FromPyObject can not be derived for unions.",
|
||||
))
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#[test]
|
||||
fn test_compile_errors() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/ui/invalid_frompy_derive.rs");
|
||||
t.compile_fail("tests/ui/invalid_macro_args.rs");
|
||||
t.compile_fail("tests/ui/invalid_property_args.rs");
|
||||
t.compile_fail("tests/ui/invalid_pyclass_args.rs");
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
use pyo3::prelude::FromPyObject;
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
struct Foo();
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
struct Foo2 {}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
enum EmptyEnum {}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
enum EnumWithEmptyTupleVar {
|
||||
EmptyTuple(),
|
||||
Valid(String),
|
||||
}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
enum EnumWithEmptyStructVar {
|
||||
EmptyStruct {},
|
||||
Valid(String),
|
||||
}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
#[pyo3(transparent)]
|
||||
struct EmptyTransparentTup();
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
#[pyo3(transparent)]
|
||||
struct EmptyTransparentStruct {}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
enum EnumWithTransparentEmptyTupleVar {
|
||||
#[pyo3(transparent)]
|
||||
EmptyTuple(),
|
||||
Valid(String),
|
||||
}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
enum EnumWithTransparentEmptyStructVar {
|
||||
#[pyo3(transparent)]
|
||||
EmptyStruct {},
|
||||
Valid(String),
|
||||
}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
#[pyo3(transparent)]
|
||||
struct TransparentTupTooManyFields(String, String);
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
#[pyo3(transparent)]
|
||||
struct TransparentStructTooManyFields {
|
||||
foo: String,
|
||||
bar: String,
|
||||
}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
enum EnumWithTransparentTupleTooMany {
|
||||
#[pyo3(transparent)]
|
||||
EmptyTuple(String, String),
|
||||
Valid(String),
|
||||
}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
enum EnumWithTransparentStructTooMany {
|
||||
#[pyo3(transparent)]
|
||||
EmptyStruct {
|
||||
foo: String,
|
||||
bar: String,
|
||||
},
|
||||
Valid(String),
|
||||
}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
struct UnknownAttribute {
|
||||
#[pyo3(attr)]
|
||||
a: String,
|
||||
}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
struct InvalidAttributeArg {
|
||||
#[pyo3(attribute(1))]
|
||||
a: String,
|
||||
}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
struct TooManyAttributeArgs {
|
||||
#[pyo3(attribute("a", "b"))]
|
||||
a: String,
|
||||
}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
struct EmptyAttributeArg {
|
||||
#[pyo3(attribute(""))]
|
||||
a: String,
|
||||
}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
struct NoAttributeArg {
|
||||
#[pyo3(attribute())]
|
||||
a: String,
|
||||
}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
struct TooManyitemArgs {
|
||||
#[pyo3(item("a", "b"))]
|
||||
a: String,
|
||||
}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
struct NoItemArg {
|
||||
#[pyo3(item())]
|
||||
a: String,
|
||||
}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
struct ItemAndAttribute {
|
||||
#[pyo3(item, attribute)]
|
||||
a: String,
|
||||
}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
#[pyo3(unknown = "should not work")]
|
||||
struct UnknownContainerAttr {
|
||||
a: String,
|
||||
}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
#[pyo3(annotation = "should not work")]
|
||||
struct AnnotationOnStruct {
|
||||
a: String,
|
||||
}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
enum InvalidAnnotatedEnum {
|
||||
#[pyo3(annotation = 1)]
|
||||
Foo(String),
|
||||
}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
enum TooManyLifetimes<'a, 'b> {
|
||||
Foo(&'a str),
|
||||
Bar(&'b str),
|
||||
}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
union Union {
|
||||
a: usize,
|
||||
}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
enum UnitEnum {
|
||||
Unit,
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,173 @@
|
|||
error: Cannot derive FromPyObject for empty structs and variants.
|
||||
--> $DIR/invalid_frompy_derive.rs:4:11
|
||||
|
|
||||
4 | struct Foo();
|
||||
| ^^
|
||||
|
||||
error: Cannot derive FromPyObject for empty structs and variants.
|
||||
--> $DIR/invalid_frompy_derive.rs:7:13
|
||||
|
|
||||
7 | struct Foo2 {}
|
||||
| ^^
|
||||
|
||||
error: Cannot derive FromPyObject for empty enum.
|
||||
--> $DIR/invalid_frompy_derive.rs:10:6
|
||||
|
|
||||
10 | enum EmptyEnum {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: Cannot derive FromPyObject for empty structs and variants.
|
||||
--> $DIR/invalid_frompy_derive.rs:14:15
|
||||
|
|
||||
14 | EmptyTuple(),
|
||||
| ^^
|
||||
|
||||
error: Cannot derive FromPyObject for empty structs and variants.
|
||||
--> $DIR/invalid_frompy_derive.rs:20:17
|
||||
|
|
||||
20 | EmptyStruct {},
|
||||
| ^^
|
||||
|
||||
error: Cannot derive FromPyObject for empty structs and variants.
|
||||
--> $DIR/invalid_frompy_derive.rs:26:27
|
||||
|
|
||||
26 | struct EmptyTransparentTup();
|
||||
| ^^
|
||||
|
||||
error: Cannot derive FromPyObject for empty structs and variants.
|
||||
--> $DIR/invalid_frompy_derive.rs:30:31
|
||||
|
|
||||
30 | struct EmptyTransparentStruct {}
|
||||
| ^^
|
||||
|
||||
error: Cannot derive FromPyObject for empty structs and variants.
|
||||
--> $DIR/invalid_frompy_derive.rs:35:15
|
||||
|
|
||||
35 | EmptyTuple(),
|
||||
| ^^
|
||||
|
||||
error: Cannot derive FromPyObject for empty structs and variants.
|
||||
--> $DIR/invalid_frompy_derive.rs:42:17
|
||||
|
|
||||
42 | EmptyStruct {},
|
||||
| ^^
|
||||
|
||||
error: Transparent structs and variants can only have 1 field
|
||||
--> $DIR/invalid_frompy_derive.rs:48:35
|
||||
|
|
||||
48 | struct TransparentTupTooManyFields(String, String);
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: Transparent structs and variants can only have 1 field
|
||||
--> $DIR/invalid_frompy_derive.rs:52:39
|
||||
|
|
||||
52 | struct TransparentStructTooManyFields {
|
||||
| _______________________________________^
|
||||
53 | | foo: String,
|
||||
54 | | bar: String,
|
||||
55 | | }
|
||||
| |_^
|
||||
|
||||
error: Transparent structs and variants can only have 1 field
|
||||
--> $DIR/invalid_frompy_derive.rs:60:15
|
||||
|
|
||||
60 | EmptyTuple(String, String),
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: Transparent structs and variants can only have 1 field
|
||||
--> $DIR/invalid_frompy_derive.rs:67:17
|
||||
|
|
||||
67 | EmptyStruct {
|
||||
| _________________^
|
||||
68 | | foo: String,
|
||||
69 | | bar: String,
|
||||
70 | | },
|
||||
| |_____^
|
||||
|
||||
error: Expected `attribute` or `item`.
|
||||
--> $DIR/invalid_frompy_derive.rs:76:12
|
||||
|
|
||||
76 | #[pyo3(attr)]
|
||||
| ^^^^
|
||||
|
||||
error: Expected a single string literal argument.
|
||||
--> $DIR/invalid_frompy_derive.rs:82:22
|
||||
|
|
||||
82 | #[pyo3(attribute(1))]
|
||||
| ^
|
||||
|
||||
error: Expected a single string literal argument.
|
||||
--> $DIR/invalid_frompy_derive.rs:88:22
|
||||
|
|
||||
88 | #[pyo3(attribute("a", "b"))]
|
||||
| ^^^^^^^^
|
||||
|
||||
error: Attribute name cannot be empty.
|
||||
--> $DIR/invalid_frompy_derive.rs:94:22
|
||||
|
|
||||
94 | #[pyo3(attribute(""))]
|
||||
| ^^
|
||||
|
||||
error: Expected a single string literal argument.
|
||||
--> $DIR/invalid_frompy_derive.rs:100:12
|
||||
|
|
||||
100 | #[pyo3(attribute())]
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: Expected a single literal argument.
|
||||
--> $DIR/invalid_frompy_derive.rs:106:17
|
||||
|
|
||||
106 | #[pyo3(item("a", "b"))]
|
||||
| ^^^^^^^^
|
||||
|
||||
error: Expected a single literal argument.
|
||||
--> $DIR/invalid_frompy_derive.rs:112:12
|
||||
|
|
||||
112 | #[pyo3(item())]
|
||||
| ^^^^^^
|
||||
|
||||
error: Only one of `item`, `attribute` can be provided, possibly with an additional argument: `item("key")` or `attribute("name").
|
||||
--> $DIR/invalid_frompy_derive.rs:118:12
|
||||
|
|
||||
118 | #[pyo3(item, attribute)]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: Expected `transparent` or `annotation = "name"`
|
||||
--> $DIR/invalid_frompy_derive.rs:123:8
|
||||
|
|
||||
123 | #[pyo3(unknown = "should not work")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: Annotating error messages for structs is not supported. Remove the annotation attribute.
|
||||
--> $DIR/invalid_frompy_derive.rs:129:1
|
||||
|
|
||||
129 | #[pyo3(annotation = "should not work")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: Expected string literal.
|
||||
--> $DIR/invalid_frompy_derive.rs:136:25
|
||||
|
|
||||
136 | #[pyo3(annotation = 1)]
|
||||
| ^
|
||||
|
||||
error: FromPyObject can be derived with at most one lifetime parameter.
|
||||
--> $DIR/invalid_frompy_derive.rs:141:22
|
||||
|
|
||||
141 | enum TooManyLifetimes<'a, 'b> {
|
||||
| ^^^^^^^^
|
||||
|
||||
error: FromPyObject can not be derived for unions.
|
||||
--> $DIR/invalid_frompy_derive.rs:147:1
|
||||
|
|
||||
147 | / union Union {
|
||||
148 | | a: usize,
|
||||
149 | | }
|
||||
| |_^
|
||||
|
||||
error: Cannot derive FromPyObject for empty structs and variants.
|
||||
--> $DIR/invalid_frompy_derive.rs:151:10
|
||||
|
|
||||
151 | #[derive(FromPyObject)]
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
Loading…
Reference in New Issue