Refactor PmpMacro trait and macro registry: replace dyn dispatch with function pointers, streamline handler struct registration, and enhance documentation
This commit is contained in:
@@ -10,7 +10,6 @@ license-file.workspace = true
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
|
||||
syn = { version = "^2", features = ["full"] }
|
||||
quote = "^1"
|
||||
darling = "^0"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
//! | `target` | [`Target`] bitflags for PHP attribute target sites |
|
||||
//! | `descriptor` | [`MacroDescriptor`] and [`ParamDescriptor`] - static metadata |
|
||||
//! | `context` | [`MacroContext`], [`AttributedNode`], [`ResolvedAttr`], etc. |
|
||||
//! | `traits` | [`PmpMacro`] trait, [`MacroRegistration`], [`validate_registry`]|
|
||||
//! | `traits` | [`PmpMacro`] trait, [`MacroRegistration`], [`MacroRegistry`] |
|
||||
|
||||
mod target;
|
||||
mod traits;
|
||||
|
||||
@@ -10,24 +10,16 @@ use crate::{
|
||||
|
||||
// --------------------------------------------------------------------------------------------- //
|
||||
|
||||
/// The core trait every PHP macro attribute handler must implement.
|
||||
/// The handler trait implemented by each PHP macro attribute struct.
|
||||
///
|
||||
/// The `descriptor` method is generated by `#[derive(PmpMacro)]` and should
|
||||
/// not be written by hand. The two handler methods have default no-op
|
||||
/// The two handler methods have default no-op
|
||||
/// implementations so that analysis-only and transform-only macros only need
|
||||
/// to override what they actually do.
|
||||
///
|
||||
/// The trait is object-safe (`&self`, no generics) so the registry can store
|
||||
/// The trait is object-safe (`&self`, no generics), so the registry can store
|
||||
/// `Box<dyn PmpMacro>` without knowing concrete types.
|
||||
pub trait PmpMacro: Send + Sync {
|
||||
// --- generated by #[derive(PmpMacro)] --------------------------------------------------- //
|
||||
|
||||
/// Returns the static metadata for this macro.
|
||||
/// Must not be implemented by hand - use `#[derive(PmpMacro)]`.
|
||||
fn descriptor(&self) -> &'static MacroDescriptor;
|
||||
|
||||
// --- user-implemented handler hooks ----------------------------------------------------- //
|
||||
|
||||
pub trait PmpMacro: Send + Sync
|
||||
{
|
||||
/// Analyse the attributed PHP node and emit diagnostics.
|
||||
///
|
||||
/// Called during the analysis pass. Returning an empty `Vec` means no
|
||||
@@ -38,9 +30,7 @@ pub trait PmpMacro: Send + Sync {
|
||||
ctx: &MacroContext,
|
||||
attr: &ResolvedAttr,
|
||||
node: &AttributedNode,
|
||||
) -> Vec<Diagnostic> {
|
||||
vec![]
|
||||
}
|
||||
) -> Vec<Diagnostic> { vec![] }
|
||||
|
||||
/// Produce rewrite instructions for the attributed PHP node.
|
||||
///
|
||||
@@ -52,25 +42,40 @@ pub trait PmpMacro: Send + Sync {
|
||||
ctx: &MacroContext,
|
||||
attr: &ResolvedAttr,
|
||||
node: &AttributedNode,
|
||||
) -> Vec<Rewrite> {
|
||||
vec![]
|
||||
}
|
||||
) -> Vec<Rewrite> { vec![] }
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------- //
|
||||
|
||||
/// Type alias for the analyze handler function pointer.
|
||||
pub type AnalyzeFn = fn(&MacroContext, &ResolvedAttr, &AttributedNode) -> Vec<Diagnostic>;
|
||||
/// Type alias for the transform handler function pointer.
|
||||
pub type TransformFn = fn(&MacroContext, &ResolvedAttr, &AttributedNode) -> Vec<Rewrite>;
|
||||
|
||||
/// An entry in the global macro registry, submitted via `inventory::submit!`.
|
||||
///
|
||||
/// Wraps a `&'static dyn PmpMacro` so the inventory iterator can hand out
|
||||
/// references to every registered handler without allocation.
|
||||
/// Stores static metadata alongside fn pointers that delegate to the
|
||||
/// handler struct's `PmpMacro` impl, avoiding the need for `dyn` dispatch.
|
||||
///
|
||||
/// Generated via `#[derive(PmpMacro)]`
|
||||
pub struct MacroRegistration {
|
||||
pub handler: &'static dyn PmpMacro,
|
||||
/// Static metadata for this macro — attribute name, targets, params, docs.
|
||||
pub descriptor: &'static MacroDescriptor,
|
||||
/// Delegates to `<Handler as PmpMacro>::analyze`.
|
||||
pub analyze: AnalyzeFn,
|
||||
/// Delegates to `<Handler as PmpMacro>::transform`.
|
||||
pub transform: TransformFn,
|
||||
}
|
||||
|
||||
impl MacroRegistration
|
||||
{
|
||||
pub const fn new(handler: &'static dyn PmpMacro) -> Self {
|
||||
Self { handler }
|
||||
pub const fn new(
|
||||
descriptor: &'static MacroDescriptor,
|
||||
analyze: AnalyzeFn,
|
||||
transform: TransformFn,
|
||||
) -> Self
|
||||
{
|
||||
Self { descriptor, analyze, transform }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,11 +84,10 @@ inventory::collect!(MacroRegistration);
|
||||
// --------------------------------------------------------------------------------------------- //
|
||||
|
||||
/// Validated, deduplicated view of the global macro registry.
|
||||
///
|
||||
/// Built once on first access and then cached for the lifetime of the process.
|
||||
/// Built once on first access via [`registry()`] and cached for the process lifetime.
|
||||
pub struct MacroRegistry {
|
||||
/// Handlers keyed by attribute name for O(1) lookup during processing.
|
||||
handlers: HashMap<&'static str, &'static dyn PmpMacro>,
|
||||
handlers: HashMap<&'static str, &'static MacroRegistration>,
|
||||
}
|
||||
|
||||
static REGISTRY: OnceLock<MacroRegistry> = OnceLock::new();
|
||||
@@ -104,14 +108,13 @@ impl MacroRegistry
|
||||
|
||||
fn build() -> Self
|
||||
{
|
||||
let mut handlers: HashMap<&'static str, &'static dyn PmpMacro> = HashMap::new();
|
||||
let mut handlers: HashMap<&'static str, &'static MacroRegistration> = HashMap::new();
|
||||
|
||||
for reg in inventory::iter::<MacroRegistration>
|
||||
{
|
||||
let name = reg.handler.descriptor().name;
|
||||
let name = reg.descriptor.name;
|
||||
|
||||
if handlers.insert(name, reg.handler).is_some()
|
||||
{
|
||||
if handlers.insert(name, reg).is_some() {
|
||||
panic!(
|
||||
"duplicate PmpMacro registration: attribute name \"{name}\" is \
|
||||
registered more than once - check your macro definitions"
|
||||
@@ -122,13 +125,13 @@ impl MacroRegistry
|
||||
Self { handlers }
|
||||
}
|
||||
|
||||
/// Look up a registered macro handler by its PHP attribute name.
|
||||
pub fn get(&self, name: &str) -> Option<&'static dyn PmpMacro> {
|
||||
/// Look up a registered macro by its PHP attribute name.
|
||||
pub fn get(&self, name: &str) -> Option<&'static MacroRegistration> {
|
||||
self.handlers.get(name).copied()
|
||||
}
|
||||
|
||||
/// Iterate over all registered handlers.
|
||||
pub fn iter(&self) -> impl Iterator<Item = &'static dyn PmpMacro> {
|
||||
/// Iterate over all registered macros.
|
||||
pub fn iter(&self) -> impl Iterator<Item = &'static MacroRegistration> {
|
||||
self.handlers.values().copied()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user