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
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
syn = { version = "^2", features = ["full"] }
|
syn = { version = "^2", features = ["full"] }
|
||||||
quote = "^1"
|
quote = "^1"
|
||||||
darling = "^0"
|
darling = "^0"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
//! | `target` | [`Target`] bitflags for PHP attribute target sites |
|
//! | `target` | [`Target`] bitflags for PHP attribute target sites |
|
||||||
//! | `descriptor` | [`MacroDescriptor`] and [`ParamDescriptor`] - static metadata |
|
//! | `descriptor` | [`MacroDescriptor`] and [`ParamDescriptor`] - static metadata |
|
||||||
//! | `context` | [`MacroContext`], [`AttributedNode`], [`ResolvedAttr`], etc. |
|
//! | `context` | [`MacroContext`], [`AttributedNode`], [`ResolvedAttr`], etc. |
|
||||||
//! | `traits` | [`PmpMacro`] trait, [`MacroRegistration`], [`validate_registry`]|
|
//! | `traits` | [`PmpMacro`] trait, [`MacroRegistration`], [`MacroRegistry`] |
|
||||||
|
|
||||||
mod target;
|
mod target;
|
||||||
mod traits;
|
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
|
/// The two handler methods have default no-op
|
||||||
/// not be written by hand. The two handler methods have default no-op
|
|
||||||
/// implementations so that analysis-only and transform-only macros only need
|
/// implementations so that analysis-only and transform-only macros only need
|
||||||
/// to override what they actually do.
|
/// 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.
|
/// `Box<dyn PmpMacro>` without knowing concrete types.
|
||||||
pub trait PmpMacro: Send + Sync {
|
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 ----------------------------------------------------- //
|
|
||||||
|
|
||||||
/// Analyse the attributed PHP node and emit diagnostics.
|
/// Analyse the attributed PHP node and emit diagnostics.
|
||||||
///
|
///
|
||||||
/// Called during the analysis pass. Returning an empty `Vec` means no
|
/// Called during the analysis pass. Returning an empty `Vec` means no
|
||||||
@@ -38,9 +30,7 @@ pub trait PmpMacro: Send + Sync {
|
|||||||
ctx: &MacroContext,
|
ctx: &MacroContext,
|
||||||
attr: &ResolvedAttr,
|
attr: &ResolvedAttr,
|
||||||
node: &AttributedNode,
|
node: &AttributedNode,
|
||||||
) -> Vec<Diagnostic> {
|
) -> Vec<Diagnostic> { vec![] }
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Produce rewrite instructions for the attributed PHP node.
|
/// Produce rewrite instructions for the attributed PHP node.
|
||||||
///
|
///
|
||||||
@@ -52,25 +42,40 @@ pub trait PmpMacro: Send + Sync {
|
|||||||
ctx: &MacroContext,
|
ctx: &MacroContext,
|
||||||
attr: &ResolvedAttr,
|
attr: &ResolvedAttr,
|
||||||
node: &AttributedNode,
|
node: &AttributedNode,
|
||||||
) -> Vec<Rewrite> {
|
) -> Vec<Rewrite> { vec![] }
|
||||||
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!`.
|
/// An entry in the global macro registry, submitted via `inventory::submit!`.
|
||||||
///
|
///
|
||||||
/// Wraps a `&'static dyn PmpMacro` so the inventory iterator can hand out
|
/// Stores static metadata alongside fn pointers that delegate to the
|
||||||
/// references to every registered handler without allocation.
|
/// handler struct's `PmpMacro` impl, avoiding the need for `dyn` dispatch.
|
||||||
|
///
|
||||||
|
/// Generated via `#[derive(PmpMacro)]`
|
||||||
pub struct MacroRegistration {
|
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
|
impl MacroRegistration
|
||||||
{
|
{
|
||||||
pub const fn new(handler: &'static dyn PmpMacro) -> Self {
|
pub const fn new(
|
||||||
Self { handler }
|
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.
|
/// Validated, deduplicated view of the global macro registry.
|
||||||
///
|
/// Built once on first access via [`registry()`] and cached for the process lifetime.
|
||||||
/// Built once on first access and then cached for the lifetime of the process.
|
|
||||||
pub struct MacroRegistry {
|
pub struct MacroRegistry {
|
||||||
/// Handlers keyed by attribute name for O(1) lookup during processing.
|
/// 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();
|
static REGISTRY: OnceLock<MacroRegistry> = OnceLock::new();
|
||||||
@@ -104,14 +108,13 @@ impl MacroRegistry
|
|||||||
|
|
||||||
fn build() -> Self
|
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>
|
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!(
|
panic!(
|
||||||
"duplicate PmpMacro registration: attribute name \"{name}\" is \
|
"duplicate PmpMacro registration: attribute name \"{name}\" is \
|
||||||
registered more than once - check your macro definitions"
|
registered more than once - check your macro definitions"
|
||||||
@@ -122,13 +125,13 @@ impl MacroRegistry
|
|||||||
Self { handlers }
|
Self { handlers }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Look up a registered macro handler by its PHP attribute name.
|
/// Look up a registered macro by its PHP attribute name.
|
||||||
pub fn get(&self, name: &str) -> Option<&'static dyn PmpMacro> {
|
pub fn get(&self, name: &str) -> Option<&'static MacroRegistration> {
|
||||||
self.handlers.get(name).copied()
|
self.handlers.get(name).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over all registered handlers.
|
/// Iterate over all registered macros.
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &'static dyn PmpMacro> {
|
pub fn iter(&self) -> impl Iterator<Item = &'static MacroRegistration> {
|
||||||
self.handlers.values().copied()
|
self.handlers.values().copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user