Skip to content

Commit 529f818

Browse files
committed
mlua_derive: Fix hygiene of callback bindings in userdata_impl
1 parent 5c1a731 commit 529f818

2 files changed

Lines changed: 83 additions & 21 deletions

File tree

mlua_derive/src/userdata/userdata_impl.rs

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::sync::atomic::{AtomicUsize, Ordering};
22

33
use proc_macro::TokenStream;
4-
use proc_macro2::TokenStream as TokenStream2;
4+
use proc_macro2::{Span as Span2, TokenStream as TokenStream2};
55
use quote::{format_ident, quote};
66
use syn::spanned::Spanned;
77
use syn::{
@@ -535,14 +535,16 @@ fn gen_arg_token(arg: &ArgInfo) -> TokenStream2 {
535535
/// Generate call arguments for invoking the original method.
536536
fn gen_call_args(info: &MethodInfo) -> TokenStream2 {
537537
let mut call_args: Vec<TokenStream2> = Vec::new();
538+
let this = Ident::new("this", Span2::mixed_site());
539+
let lua = Ident::new("lua", Span2::mixed_site());
538540

539541
match info.self_kind {
540542
SelfKind::None => {}
541-
_ => call_args.push(quote! { this }),
543+
_ => call_args.push(quote! { #this }),
542544
}
543545

544546
if info.lua.is_some() {
545-
call_args.push(quote! { lua });
547+
call_args.push(quote! { #lua });
546548
}
547549

548550
for arg in &info.args {
@@ -555,17 +557,19 @@ fn gen_call_args(info: &MethodInfo) -> TokenStream2 {
555557
/// Generate call arguments for invoking the original async method.
556558
fn gen_async_call_args(info: &MethodInfo) -> TokenStream2 {
557559
let mut call_args: Vec<TokenStream2> = Vec::new();
560+
let this = Ident::new("this", Span2::mixed_site());
561+
let lua = Ident::new("lua", Span2::mixed_site());
558562

559563
match info.self_kind {
560564
SelfKind::None => {}
561-
SelfKind::Ref(RefKind::Mut | RefKind::OptionMut) => call_args.push(quote! { &mut this }),
562-
SelfKind::Ref(_) => call_args.push(quote! { &this }),
563-
SelfKind::Owned => call_args.push(quote! { this }),
565+
SelfKind::Ref(RefKind::Mut | RefKind::OptionMut) => call_args.push(quote! { &mut #this }),
566+
SelfKind::Ref(_) => call_args.push(quote! { &#this }),
567+
SelfKind::Owned => call_args.push(quote! { #this }),
564568
}
565569

566570
match info.lua {
567-
Some(LuaArg::Ref) => call_args.push(quote! { &lua }),
568-
Some(LuaArg::Owned) => call_args.push(quote! { lua }),
571+
Some(LuaArg::Ref) => call_args.push(quote! { &#lua }),
572+
Some(LuaArg::Owned) => call_args.push(quote! { #lua }),
569573
None => {}
570574
}
571575

@@ -579,19 +583,25 @@ fn gen_async_call_args(info: &MethodInfo) -> TokenStream2 {
579583
/// Generate the closure params for the registration callback.
580584
fn gen_closure_params(info: &MethodInfo) -> TokenStream2 {
581585
let destructure = gen_closure_destructure(info);
586+
let this = Ident::new("this", Span2::mixed_site());
587+
let lua = Ident::new("lua", Span2::mixed_site());
588+
582589
match info.self_kind {
583-
SelfKind::None => quote! { |lua, #destructure| },
584-
_ => quote! { |lua, this, #destructure| },
590+
SelfKind::None => quote! { |#lua, #destructure| },
591+
_ => quote! { |#lua, #this, #destructure| },
585592
}
586593
}
587594

588595
/// Generate the closure params for an async registration callback.
589596
fn gen_async_closure_params(info: &MethodInfo) -> TokenStream2 {
590597
let destructure = gen_closure_destructure(info);
598+
let this = Ident::new("this", Span2::mixed_site());
599+
let lua = Ident::new("lua", Span2::mixed_site());
600+
591601
match info.self_kind {
592-
SelfKind::None => quote! { |lua, #destructure| },
593-
SelfKind::Ref(RefKind::Mut) => quote! { |lua, mut this, #destructure| },
594-
_ => quote! { |lua, this, #destructure| },
602+
SelfKind::None => quote! { |#lua, #destructure| },
603+
SelfKind::Ref(RefKind::Mut) => quote! { |#lua, mut #this, #destructure| },
604+
_ => quote! { |#lua, #this, #destructure| },
595605
}
596606
}
597607

@@ -603,19 +613,21 @@ fn gen_field_getter(
603613
) -> TokenStream2 {
604614
let lua_name = lua_attr.name(fn_name);
605615
let call_args = gen_call_args(info);
616+
let this = Ident::new("this", Span2::mixed_site());
617+
let lua = Ident::new("lua", Span2::mixed_site());
606618

607619
if lua_attr.infallible {
608620
return quote! {
609-
registry.add_field_method_get(#lua_name, |lua, this| {
610-
let _ = lua; // silence unused variable warning
621+
registry.add_field_method_get(#lua_name, |#lua, #this| {
622+
let _ = #lua; // silence unused variable warning
611623
Ok(#type_path::#fn_name(#call_args))
612624
});
613625
};
614626
}
615627

616628
quote! {
617-
registry.add_field_method_get(#lua_name, |lua, this| {
618-
let _ = lua; // silence unused variable warning
629+
registry.add_field_method_get(#lua_name, |#lua, #this| {
630+
let _ = #lua; // silence unused variable warning
619631
#type_path::#fn_name(#call_args)
620632
});
621633
}
@@ -629,21 +641,23 @@ fn gen_field_setter(
629641
) -> TokenStream2 {
630642
let lua_name = lua_attr.name(fn_name);
631643
let call_args = gen_call_args(info);
644+
let this = Ident::new("this", Span2::mixed_site());
645+
let lua = Ident::new("lua", Span2::mixed_site());
632646

633647
if lua_attr.infallible {
634648
let val_ident = info.args.first().map(|a| &a.ident);
635649
return quote! {
636-
registry.add_field_method_set(#lua_name, |lua, this, #val_ident| {
637-
let _ = lua; // silence unused variable warning
650+
registry.add_field_method_set(#lua_name, |#lua, #this, #val_ident| {
651+
let _ = #lua; // silence unused variable warning
638652
Ok(#type_path::#fn_name(#call_args))
639653
});
640654
};
641655
}
642656

643657
let val_ident = info.args.first().map(|a| &a.ident);
644658
quote! {
645-
registry.add_field_method_set(#lua_name, |lua, this, #val_ident| {
646-
let _ = lua; // silence unused variable warning
659+
registry.add_field_method_set(#lua_name, |#lua, #this, #val_ident| {
660+
let _ = #lua; // silence unused variable warning
647661
#type_path::#fn_name(#call_args)
648662
});
649663
}

tests/userdata_macro.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,48 @@ fn test_static_metamethods() {
471471
.unwrap();
472472
}
473473

474+
#[derive(Clone, Debug, UserData)]
475+
struct Hygiene {
476+
value: i32,
477+
}
478+
479+
#[mlua::userdata_impl]
480+
impl Hygiene {
481+
#[lua(infallible)]
482+
fn new(value: i32) -> Self {
483+
Hygiene { value }
484+
}
485+
486+
// `this` must not clash with the generated receiver binding.
487+
#[lua(infallible)]
488+
fn add_this(&self, this: i32) -> i32 {
489+
self.value + this
490+
}
491+
492+
// `lua` must not clash either.
493+
#[lua(infallible)]
494+
fn add_lua(&self, lua: i32) -> i32 {
495+
self.value + lua
496+
}
497+
}
498+
499+
#[test]
500+
fn test_param_name_hygiene() {
501+
let lua = Lua::new();
502+
lua.globals()
503+
.set("Hygiene", lua.create_proxy::<Hygiene>().unwrap())
504+
.unwrap();
505+
lua.load(
506+
r#"
507+
local h = Hygiene.new(10)
508+
assert(h:add_this(5) == 15, "add_this should be 10 + 5 = 15")
509+
assert(h:add_lua(3) == 13, "add_lua should be 10 + 3 = 13")
510+
"#,
511+
)
512+
.exec()
513+
.unwrap();
514+
}
515+
474516
#[cfg(feature = "async")]
475517
mod async_tests {
476518
use mlua::{Lua, Result, UserData};
@@ -507,6 +549,10 @@ mod async_tests {
507549
Ok(self.0 * factor)
508550
}
509551

552+
async fn add(&self, this: u64, lua: u64) -> Result<u64> {
553+
Ok(self.0 + this + lua)
554+
}
555+
510556
async fn default_value() -> Result<u64> {
511557
Ok(42)
512558
}
@@ -541,6 +587,8 @@ mod async_tests {
541587
assert(val == 10, "expected 10, got " .. tostring(val))
542588
local doubled = c:multiply(3)
543589
assert(doubled == 30, "expected 30, got " .. tostring(doubled))
590+
local summed = c:add(1, 2)
591+
assert(summed == 13, "expected 10 + 1 + 2 = 13, got " .. tostring(summed))
544592
local inf = c:get_value_infallible()
545593
assert(inf == 10, "expected infallible 10, got " .. tostring(inf))
546594
"#,

0 commit comments

Comments
 (0)