Refactor macro handling: add pascal_to_snake utility, rewrite Target handling, and update descriptor structure for bits-based targets.

This commit is contained in:
2026-03-05 21:16:45 +01:00
parent 36b065ddb2
commit a238b63e51
5 changed files with 82 additions and 48 deletions

View File

@@ -3,6 +3,22 @@ use proc_macro2::TokenStream as TokenStream2;
use crate::attrs::MacroInput;
fn rewrite_bitor_target_expr(expr: syn::Expr) -> syn::Expr
{
match expr
{
syn::Expr::Binary(mut bin) if matches!(bin.op, syn::BinOp::BitOr(_)) => {
bin.left = Box::new(rewrite_bitor_target_expr(*bin.left));
bin.right = Box::new(rewrite_bitor_target_expr(*bin.right));
syn::Expr::Binary(bin)
}
other => {
syn::parse_quote! { (#other).bits() }
}
}
}
pub fn expand(parsed: MacroInput) -> syn::Result<TokenStream2>
{
let struct_ident = &parsed.ident;
@@ -16,8 +32,8 @@ pub fn expand(parsed: MacroInput) -> syn::Result<TokenStream2>
.unwrap_or_else(|| struct_ident.to_string());
let target_expr: syn::Expr = parsed.target
.map(|e| e.0)
.unwrap_or_else(|| syn::parse_quote! { ::pmp_macro_misc::Target::ALL });
.map(|e| rewrite_bitor_target_expr(e.0))
.unwrap_or_else(|| syn::parse_quote! { ::pmp_macro::Target::ALL.bits() });
let docs: String = parsed.docs.map(|s| s.0).unwrap_or_default();
@@ -53,7 +69,7 @@ pub fn expand(parsed: MacroInput) -> syn::Result<TokenStream2>
};
param_tokens.push(quote_spanned! { span =>
::pmp_macro_misc::ParamDescriptor {
::pmp_macro::ParamDescriptor {
name: #field_name,
php_type: #php_type,
required: #required,
@@ -64,47 +80,48 @@ pub fn expand(parsed: MacroInput) -> syn::Result<TokenStream2>
if let Some(e) = errors { return Err(e); }
let mod_ident = format_ident!("__pmp_macro_impl_{}", struct_ident);
let handler = quote! { super::#struct_ident };
let snaked_ident = pmp_core::pascal_to_snake(struct_ident.to_string().as_str());
let mod_ident = format_ident!("__pmp_macro_impl_{}", snaked_ident);
let handler = quote! { super::#struct_ident };
Ok(quote!
{
#[doc(hidden)]
mod #mod_ident
{
use ::pmp_macro_misc::{
AnalyzeFn, MacroDescriptor, MacroRegistration, ParamDescriptor,
TransformFn, PmpMacro,
use ::pmp_macro::{
AnalyzeFn, MacroDescriptor, MacroRegistration,
ParamDescriptor, Target, TransformFn, PmpMacro,
};
static PARAMS: &[ParamDescriptor] = &[ #(#param_tokens),* ];
static DESCRIPTOR: MacroDescriptor = MacroDescriptor {
name: #attr_name,
target: #target_expr,
docs: #docs,
params: PARAMS,
name: #attr_name,
target_bits: #target_expr,
docs: #docs,
params: PARAMS,
};
fn analyze(
ctx: &::pmp_macro_misc::MacroContext,
attr: &::pmp_macro_misc::ResolvedAttr,
node: &::pmp_macro_misc::AttributedNode,
) -> ::std::vec::Vec<::pmp_macro_misc::Diagnostic>
ctx: &::pmp_macro::MacroContext,
attr: &::pmp_macro::ResolvedAttr,
node: &::pmp_macro::AttributedNode,
) -> ::std::vec::Vec<::pmp_macro::Diagnostic>
{
<#handler as PmpMacro>::analyze(&::std::default::Default::default(), ctx, attr, node)
}
fn transform(
ctx: &::pmp_macro_misc::MacroContext,
attr: &::pmp_macro_misc::ResolvedAttr,
node: &::pmp_macro_misc::AttributedNode,
) -> ::std::vec::Vec<::pmp_macro_misc::Rewrite>
ctx: &::pmp_macro::MacroContext,
attr: &::pmp_macro::ResolvedAttr,
node: &::pmp_macro::AttributedNode,
) -> ::std::vec::Vec<::pmp_macro::Rewrite>
{
<#handler as PmpMacro>::transform(&::std::default::Default::default(), ctx, attr, node)
}
::inventory::submit! {
::pmp_macro::inventory::submit! {
MacroRegistration::new(&DESCRIPTOR, analyze, transform)
}
}

View File

@@ -1,36 +1,26 @@
use crate::target::Target;
/// Describes a single constructor parameter of a PHP macro attribute.
///
/// All fields are `&'static str` so the descriptor can be embedded as a
/// `static` and referenced from the inventory without allocation.
#[derive(Debug, Clone, Copy)]
pub struct ParamDescriptor {
/// The parameter name as it appears in the PHP constructor.
pub name: &'static str,
/// A human-readable PHP type hint, e.g. `"class-string[]"` or `"bool"`.
pub php_type: &'static str,
/// Whether the parameter must be supplied by the caller.
pub required: bool,
/// The PHP source representation of the default value, e.g. `"[]"` or `"false"`.
/// `None` when `required` is `true`.
pub default: Option<&'static str>,
}
/// Static metadata for a registered PHP macro attribute.
///
/// Generated by `#[derive(PmpMacro)]` and stored as a `&'static MacroDescriptor`
/// in the inventory. Entirely allocation-free.
#[derive(Debug)]
pub struct MacroDescriptor {
/// The PHP attribute name, e.g. `"Sealed"`. Used to match against
/// attribute usages in parsed PHP files.
pub name: &'static str,
/// Which PHP constructs this attribute is permitted to annotate.
pub target: Target,
/// Human-readable description of what this macro does.
/// Used for error messages and generated documentation.
pub docs: &'static str,
/// The constructor parameters this attribute accepts.
pub params: &'static [ParamDescriptor],
pub name: &'static str,
/// Raw bitflags value - use `.target()` to get a `Target`.
/// Stored as `u16` so the static initializer remains `const`.
pub target_bits: u16,
pub docs: &'static str,
pub params: &'static [ParamDescriptor],
}
impl MacroDescriptor
{
pub fn target(&self) -> Target {
Target::from_bits_truncate(self.target_bits)
}
}

View File

@@ -14,7 +14,10 @@ mod traits;
mod context;
mod descriptor;
pub use target::Target;
pub use descriptor::{ MacroDescriptor, ParamDescriptor };
pub use traits::{ PmpMacro, MacroRegistry, MacroRegistration, };
pub use context::{ Rewrite, ResolvedAttr, Diagnostic, MacroContext, AttributedNode };
#[doc(hidden)]
pub use inventory;
pub use target::*;
pub use traits::*;
pub use context::*;
pub use descriptor::*;