diff options
Diffstat (limited to 'mingling_macros/src/attr/renderer.rs')
| -rw-r--r-- | mingling_macros/src/attr/renderer.rs | 75 |
1 files changed, 49 insertions, 26 deletions
diff --git a/mingling_macros/src/attr/renderer.rs b/mingling_macros/src/attr/renderer.rs index c7cbb0b..038edd8 100644 --- a/mingling_macros/src/attr/renderer.rs +++ b/mingling_macros/src/attr/renderer.rs @@ -1,7 +1,7 @@ use proc_macro::TokenStream; use quote::{ToTokens, quote}; use syn::spanned::Spanned; -use syn::{ItemFn, ReturnType, Signature, TypePath, parse_macro_input}; +use syn::{ItemFn, Pat, ReturnType, Signature, TypePath, parse_macro_input}; use crate::get_global_set; use crate::res_injection::{extract_args_info, generate_immut_resource_bindings}; @@ -15,9 +15,7 @@ fn extract_user_return_type(sig: &Signature) -> Option<proc_macro2::TokenStream> } #[allow(clippy::too_many_lines)] -pub(crate) fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream { - // #[renderer] takes no arguments; always use the default program path - let _ = attr; +pub(crate) fn renderer_attr(item: TokenStream) -> TokenStream { let program_path = crate::default_program_path(); let program_type = &program_path; @@ -31,8 +29,8 @@ pub(crate) fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream .into(); } - // Extract the previous type, parameter name, and resource injection params - let (prev_param, previous_type, resources) = match extract_args_info(&input_fn.sig) { + // Extract the previous type and resource injection params + let (_, previous_type, resources) = match extract_args_info(&input_fn.sig) { Ok(info) => info, Err(e) => return e.to_compile_error().into(), }; @@ -69,37 +67,59 @@ pub(crate) fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream let immut_resource_stmts = generate_immut_resource_bindings(resources.iter(), program_type); let mut_resources: Vec<_> = resources.iter().filter(|r| r.is_mut).collect(); - let inner_body_with_resources = if has_mut_resources { - let mut wrapped = quote! { #(#fn_body_stmts)* }; + // Build the call to the original function with resource arguments injected + let resource_args: Vec<_> = resources + .iter() + .map(|res| { + let var_name = &res.var_name; + quote! { #var_name } + }) + .collect(); + + // Use a fixed parameter name `prev` for the trait method, regardless of + // the user's original parameter name (which may be `_` and cannot be + // referenced in expression position). + let fixed_prev: Pat = syn::parse_quote!(prev); + + let fn_call = if has_resources { + quote! { #fn_name(#fixed_prev, #(#resource_args),*) } + } else { + quote! { #fn_name(#fixed_prev) } + }; + + // Wrap the function call with modify_res for mutable resources + let inner_call = if has_mut_resources { + let mut wrapped = fn_call; for res in mut_resources.iter().rev() { let var_name = &res.var_name; let inner_type = &res.inner_type; wrapped = quote! { - ::mingling::this::<#program_type>().modify_res(|#var_name: &mut #inner_type| { + ::mingling::this::<#program_type>() + .modify_res(|#var_name: &mut #inner_type| { #wrapped }) }; } wrapped } else { - quote! { #(#fn_body_stmts)* } + fn_call }; - // Build the Renderer::render body with resource injection - // The user's body now directly creates and returns a RenderResult. + // Build the Renderer::render body with resource injection. + // The trait method injects resources and calls the original function. let render_fn_body = if has_resources { quote! { #(#immut_resource_stmts)* - #inner_body_with_resources + #inner_call } } else { - quote! { #inner_body_with_resources } + quote! { #inner_call } }; // The original function preserves the user's exact signature and body. // Resource parameters are passed directly by the caller, NOT injected from context. let original_inputs = input_fn.sig.inputs.clone(); - let original_return_type = user_return_type.clone().unwrap_or(quote! { () }); + let original_return_type = user_return_type.unwrap_or_else(|| quote! { () }); let expanded = quote! { #(#fn_attrs)* @@ -112,14 +132,13 @@ pub(crate) fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream impl ::mingling::Renderer for #struct_name { type Previous = #previous_type; - fn render(#prev_param: Self::Previous) -> ::mingling::RenderResult { + fn render(#fixed_prev: Self::Previous) -> ::mingling::RenderResult { let __renderer_result = { #render_fn_body }; ::std::convert::Into::into(__renderer_result) } } // Keep the original function unchanged - #[allow(dead_code)] #(#fn_attrs)* #vis fn #fn_name(#original_inputs) -> #original_return_type { #(#fn_body_stmts)* @@ -228,14 +247,15 @@ pub(crate) fn register_renderer(input: TokenStream) -> TokenStream { } } // renderers lock released here - let mut renderers = get_global_set(&crate::RENDERERS).lock().unwrap(); - let mut renderer_exist = get_global_set(&crate::RENDERERS_EXIST).lock().unwrap(); - - #[cfg(feature = "structural_renderer")] - let mut structural_renderers = get_global_set(&crate::STRUCTURAL_RENDERERS).lock().unwrap(); - - renderers.insert(renderer_entry_str); - renderer_exist.insert(renderer_exist_entry_str); + // Insert renderer registration directly without holding a lock variable + get_global_set(&crate::RENDERERS) + .lock() + .unwrap() + .insert(renderer_entry_str); + get_global_set(&crate::RENDERERS_EXIST) + .lock() + .unwrap() + .insert(renderer_exist_entry_str); // Only register structural renderer if the type is in STRUCTURED_TYPES #[cfg(feature = "structural_renderer")] @@ -245,7 +265,10 @@ pub(crate) fn register_renderer(input: TokenStream) -> TokenStream { .unwrap() .contains(&variant_name); if is_structured { - structural_renderers.insert(structural_renderer_entry_str); + get_global_set(&crate::STRUCTURAL_RENDERERS) + .lock() + .unwrap() + .insert(structural_renderer_entry_str); } } |
