mlua_derive: Switch from #[userdata] to [derive(UserData)]

This commit is contained in:
Alex Orlenko
2026-05-30 13:11:26 +01:00
parent 1f3dafa564
commit b7c98ad9bb
33 changed files with 85 additions and 131 deletions
+2 -3
View File
@@ -1,7 +1,6 @@
use mlua::{Lua, Result, chunk};
use mlua::{Lua, Result, UserData, chunk};
#[derive(Default)]
#[mlua::userdata]
#[derive(Default, UserData)]
struct Rectangle {
length: u32,
width: u32,
+4 -4
View File
@@ -33,11 +33,11 @@ pub fn from_lua(input: TokenStream) -> TokenStream {
from_lua::from_lua(input)
}
/// Attribute macro for exposing a Rust type as a Lua userdata.
/// Derive macro for implementing `UserData` for a Rust type.
#[cfg(feature = "macros")]
#[proc_macro_attribute]
pub fn userdata(attr: TokenStream, item: TokenStream) -> TokenStream {
userdata::userdata_type(attr, item)
#[proc_macro_derive(UserData, attributes(lua))]
pub fn userdata(item: TokenStream) -> TokenStream {
userdata::userdata_type(item)
}
/// Attribute macro for exposing impl block methods to Lua userdata.
+7 -31
View File
@@ -34,35 +34,18 @@ fn parse_field_lua_attr(attrs: &[Attribute]) -> syn::Result<LuaAttr> {
Ok(lua_attr)
}
/// Strip `#[lua(...)]` attributes from a field, keeping all others.
fn strip_lua_attrs(attrs: &[Attribute]) -> Vec<Attribute> {
(attrs.iter())
.filter(|attr| !attr.path().is_ident("lua"))
.cloned()
.collect()
}
pub fn userdata_type(attr: TokenStream, item: TokenStream) -> TokenStream {
if !attr.is_empty() {
return Error::new_spanned(
proc_macro2::TokenStream::from(attr),
"`#[userdata]` does not accept arguments",
)
.to_compile_error()
.into();
}
let mut input = parse_macro_input!(item as DeriveInput);
pub fn userdata_type(item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as DeriveInput);
let type_name = &input.ident;
let mut named_fields: Option<&mut FieldsNamed> = match &mut input.data {
Data::Struct(data) => match &mut data.fields {
let named_fields: Option<&FieldsNamed> = match &input.data {
Data::Struct(data) => match &data.fields {
Fields::Named(fields) => Some(fields),
Fields::Unnamed(_) | Fields::Unit => None,
},
Data::Enum(_) => None,
Data::Union(_) => {
return Error::new_spanned(&input, "`#[userdata]` cannot be applied to unions")
return Error::new_spanned(&input, "`#[derive(UserData)]` cannot be applied to unions")
.to_compile_error()
.into();
}
@@ -73,14 +56,14 @@ pub fn userdata_type(attr: TokenStream, item: TokenStream) -> TokenStream {
if has_type_params {
return Error::new_spanned(
&input.generics,
"`#[userdata]` does not support generic type parameters. Wrap the generic type in a concrete newtype instead."
"`#[derive(UserData)]` does not support generic type parameters. Wrap the generic type in a concrete newtype instead."
)
.to_compile_error()
.into();
}
let mut field_registrations = Vec::new();
if let Some(fields) = &mut named_fields {
if let Some(fields) = &named_fields {
for field in &fields.named {
let field_name = field.ident.as_ref().unwrap();
@@ -114,19 +97,12 @@ pub fn userdata_type(attr: TokenStream, item: TokenStream) -> TokenStream {
field_registrations.push(with_cfg(tokens, &field.attrs));
}
}
// Strip mlua-specific attributes from fields before re-emitting
for field in &mut fields.named {
field.attrs = strip_lua_attrs(&field.attrs);
}
}
let registration_type_name = format_ident!("__MluaUserDataRegistration_{type_name}");
let register_fields_fn_name = format_ident!("__mlua_register_{type_name}_fields");
let output = quote! {
#input
#[doc(hidden)]
#[allow(non_camel_case_types)]
struct #registration_type_name {
+3 -5
View File
@@ -233,16 +233,14 @@ pub use mlua_derive::chunk;
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
pub use mlua_derive::FromLua;
/// Attribute macro for exposing a struct as Lua userdata.
/// Derive macro for implementing [`UserData`] for a Rust type.
///
/// All fields are auto-exposed as get/set.
/// Named fields are auto-exposed as get/set.
///
/// Use `#[lua(...)]` to customize.
///
/// This generates a [`UserData`] trait implementation.
#[cfg(feature = "macros")]
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
pub use mlua_derive::userdata;
pub use mlua_derive::UserData;
/// Attribute macro for exposing impl block methods to Lua userdata.
///
+1 -2
View File
@@ -1,5 +1,4 @@
#[derive(Default)]
#[mlua::userdata]
#[derive(Default, mlua::UserData)]
struct Foo;
#[mlua::userdata_impl]
+2 -2
View File
@@ -1,5 +1,5 @@
error: const items do not support `getter` or `setter`
--> tests/compile/userdata_const_getter.rs:8:11
--> tests/compile/userdata_const_getter.rs:7:11
|
8 | const X: u32 = 42;
7 | const X: u32 = 42;
| ^
+1 -2
View File
@@ -1,7 +1,6 @@
use mlua::Result;
#[derive(Clone, Debug)]
#[mlua::userdata]
#[derive(Clone, Debug, mlua::UserData)]
struct Foo;
#[mlua::userdata_impl]
+4 -4
View File
@@ -1,8 +1,8 @@
error: async field function is not supported
--> tests/compile/userdata_field_async.rs:10:5
|
10 | async fn description() -> Result<String> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
--> tests/compile/userdata_field_async.rs:9:5
|
9 | async fn description() -> Result<String> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: unused import: `mlua::Result`
--> tests/compile/userdata_field_async.rs:1:5
+1 -2
View File
@@ -1,5 +1,4 @@
#[derive(Default)]
#[mlua::userdata]
#[derive(Default, mlua::UserData)]
struct Foo {
x: u32,
}
@@ -1,5 +1,5 @@
error: field function must not take arguments
--> tests/compile/userdata_field_with_args.rs:10:5
|
10 | fn as_name(name: &str) -> String {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
--> tests/compile/userdata_field_with_args.rs:9:5
|
9 | fn as_name(name: &str) -> String {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+1 -2
View File
@@ -1,5 +1,4 @@
#[derive(Default)]
#[mlua::userdata]
#[derive(Default, mlua::UserData)]
struct Foo;
#[mlua::userdata_impl]
@@ -1,5 +1,5 @@
error: `meta` can only be combined with `field`
--> tests/compile/userdata_getter_and_meta.rs:8:5
--> tests/compile/userdata_getter_and_meta.rs:7:5
|
8 | fn bar(&self) -> mlua::Result<u32> {
7 | fn bar(&self) -> mlua::Result<u32> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+1 -2
View File
@@ -1,5 +1,4 @@
#[derive(Default)]
#[mlua::userdata]
#[derive(Default, mlua::UserData)]
struct Foo {
x: u32,
}
@@ -1,5 +1,5 @@
error: at most one of `getter`, `setter`, `field` can be specified
--> tests/compile/userdata_getter_and_setter.rs:10:5
|
10 | fn x(&self) -> mlua::Result<u32> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
--> tests/compile/userdata_getter_and_setter.rs:9:5
|
9 | fn x(&self) -> mlua::Result<u32> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+1 -2
View File
@@ -1,7 +1,6 @@
use mlua::Result;
#[derive(Clone, Debug)]
#[mlua::userdata]
#[derive(Clone, Debug, mlua::UserData)]
struct Foo(u64);
#[mlua::userdata_impl]
+4 -4
View File
@@ -1,8 +1,8 @@
error: async field getter is not supported
--> tests/compile/userdata_getter_async.rs:10:5
|
10 | async fn value(&self) -> Result<u64> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
--> tests/compile/userdata_getter_async.rs:9:5
|
9 | async fn value(&self) -> Result<u64> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: unused import: `mlua::Result`
--> tests/compile/userdata_getter_async.rs:1:5
+1 -2
View File
@@ -1,5 +1,4 @@
#[derive(Default)]
#[mlua::userdata]
#[derive(Default, mlua::UserData)]
struct Foo {
x: u32,
}
@@ -1,5 +1,5 @@
error: field getter must not take additional arguments
--> tests/compile/userdata_getter_extra_arg.rs:10:5
|
10 | fn x(&self, extra: u32) -> mlua::Result<u32> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
--> tests/compile/userdata_getter_extra_arg.rs:9:5
|
9 | fn x(&self, extra: u32) -> mlua::Result<u32> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+1 -2
View File
@@ -1,5 +1,4 @@
#[derive(Default)]
#[mlua::userdata]
#[derive(Default, mlua::UserData)]
struct Foo {
x: u32,
}
@@ -1,5 +1,5 @@
error: field getter must take `&self`
--> tests/compile/userdata_getter_mut_self.rs:10:5
|
10 | fn x(&mut self) -> mlua::Result<u32> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
--> tests/compile/userdata_getter_mut_self.rs:9:5
|
9 | fn x(&mut self) -> mlua::Result<u32> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+1 -2
View File
@@ -1,5 +1,4 @@
#[derive(Default)]
#[mlua::userdata]
#[derive(Default, mlua::UserData)]
struct Foo;
#[mlua::userdata_impl]
@@ -1,5 +1,5 @@
error: meta methods cannot take `self`, use `&[mut] self` instead
--> tests/compile/userdata_meta_owned_self.rs:8:5
--> tests/compile/userdata_meta_owned_self.rs:7:5
|
8 | fn __gc(self) -> mlua::Result<()> {
7 | fn __gc(self) -> mlua::Result<()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+1 -2
View File
@@ -1,5 +1,4 @@
#[derive(Default)]
#[mlua::userdata]
#[derive(Default, mlua::UserData)]
struct Foo(Vec<u8>);
#[mlua::userdata_impl]
+2 -2
View File
@@ -1,5 +1,5 @@
error: this reference type is not supported as a callback parameter
--> tests/compile/userdata_mut_slice_arg.rs:7:27
--> tests/compile/userdata_mut_slice_arg.rs:6:27
|
7 | fn first(&self, data: &mut [u8]) -> mlua::Result<u8> {
6 | fn first(&self, data: &mut [u8]) -> mlua::Result<u8> {
| ^^^^^^^^^
+1 -2
View File
@@ -1,7 +1,6 @@
use mlua::Result;
#[derive(Clone, Debug)]
#[mlua::userdata]
#[derive(Clone, Debug, mlua::UserData)]
struct Foo(u64);
#[mlua::userdata_impl]
+4 -4
View File
@@ -1,8 +1,8 @@
error: async field setter is not supported
--> tests/compile/userdata_setter_async.rs:10:5
|
10 | async fn set_value(&mut self, val: u64) -> Result<()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
--> tests/compile/userdata_setter_async.rs:9:5
|
9 | async fn set_value(&mut self, val: u64) -> Result<()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: unused import: `mlua::Result`
--> tests/compile/userdata_setter_async.rs:1:5
+1 -2
View File
@@ -1,5 +1,4 @@
#[derive(Default)]
#[mlua::userdata]
#[derive(Default, mlua::UserData)]
struct Foo {
x: u32,
}
@@ -1,5 +1,5 @@
error: field setter must take exactly one value argument
--> tests/compile/userdata_setter_no_value.rs:10:5
|
10 | fn set_x(&mut self) -> mlua::Result<()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
--> tests/compile/userdata_setter_no_value.rs:9:5
|
9 | fn set_x(&mut self) -> mlua::Result<()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+1 -2
View File
@@ -1,5 +1,4 @@
#[derive(Default)]
#[mlua::userdata]
#[derive(Default, mlua::UserData)]
struct Foo {
x: u32,
}
@@ -1,5 +1,5 @@
error: field setter must take `&[mut] self`
--> tests/compile/userdata_setter_ref_self.rs:10:5
|
10 | fn set_x(self, val: u32) -> mlua::Result<()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
--> tests/compile/userdata_setter_ref_self.rs:9:5
|
9 | fn set_x(self, val: u32) -> mlua::Result<()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+1 -2
View File
@@ -1,5 +1,4 @@
#[derive(Default)]
#[mlua::userdata]
#[derive(Default, mlua::UserData)]
struct Foo {
x: u32,
}
@@ -1,5 +1,5 @@
error: field function must not take `self`
--> tests/compile/userdata_static_with_self.rs:10:5
|
10 | fn get_x(&self) -> mlua::Result<u32> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
--> tests/compile/userdata_static_with_self.rs:9:5
|
9 | fn get_x(&self) -> mlua::Result<u32> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+7 -12
View File
@@ -1,9 +1,8 @@
#![cfg(feature = "macros")]
use mlua::{Lua, Result};
use mlua::{Lua, Result, UserData};
#[derive(Default, Clone, Debug)]
#[mlua::userdata]
#[derive(Default, Clone, Debug, UserData)]
struct Rectangle {
length: u32,
#[lua]
@@ -208,8 +207,7 @@ fn test_rectangle() {
.unwrap();
}
#[derive(Clone, Debug)]
#[mlua::userdata]
#[derive(Clone, Debug, UserData)]
enum Color {
Red,
Green,
@@ -274,8 +272,7 @@ fn test_color() {
.unwrap();
}
#[derive(Clone, Debug)]
#[mlua::userdata]
#[derive(Clone, Debug, UserData)]
struct Point(i32, i32);
fn make_lua_point() -> Lua {
@@ -328,8 +325,7 @@ fn test_point() {
.unwrap();
}
#[derive(Clone, Debug)]
#[mlua::userdata]
#[derive(Clone, Debug, UserData)]
struct Bytes(Vec<u8>);
#[mlua::userdata_impl]
@@ -375,10 +371,9 @@ fn test_known_borrow_wrappers() -> Result<()> {
#[cfg(feature = "async")]
mod async_tests {
use mlua::{Lua, Result};
use mlua::{Lua, Result, UserData};
#[derive(Clone, Debug)]
#[mlua::userdata]
#[derive(Clone, Debug, UserData)]
struct AsyncCounter(u64);
#[mlua::userdata_impl]