#macro #visibility #macro-rules #namespaced #decl-macro

macro macro_pub

polyfill proc-macro for visibility scoped macros

1 unstable release

0.1.0 Apr 15, 2022

#15 in #visibility

49 downloads per month
Used in 4 crates (via awaur)

MIT/Apache

19KB
243 lines

#[macro_pub] is a replacement for #[macro_export] which provides normal visibility rules for macro_rules! macros.

If you want the macro to not have world-public visibility, then use pub(in path) syntax in the attribute, e.g. #[macro_pub(crate)].

How

If you use a legacy scoped macro, it "mounts" it to the normal visibility and can be used as a normal item from then on. So when you write

#[macro_pub(crate)]
macro_rules! my_macro {
    () => {};
}

it expands to something like

macro_rules! macro_impl_279137529572831871236407390024221977230_my_macro {
    () => {};
}
pub(crate) use macro_impl_279137529572831871236407390024221977230_my_macro
    as my_macro;

The hash is the XXH3 hash of the annotated item's TokenStream, and is included to prevent name conflicts in the macro namespace.

If you do not specify a pub(in path) restriction, you instead get a world-visible macro:

#[macro_export]
#[doc(hidden)]
macro_rules! macro_impl_279137529572831871236407390024221977230_my_macro {
    () => {};
}
pub use macro_impl_279137529572831871236407390024221977230_my_macro
    as my_macro;

Documenting public macros

Unfortunately, #[doc(hidden)] on the actual macro implementation hides any documentation attatched to it, #[doc(inline)]ing the use juts makes it hidden as well, and you can't attach documentation. Thus, on stable, your macro will be included in the documentation just as a re-export:

pub use macro_impl_279137529572831871236407390024221977230_my_macro
    as my_macro;

and macro_impl_279137529572831871236407390024221977230_my_macro will not be documented.

If you are on nightly, however, we can take advantage of nightly features in order to document the macro. In order to document your crate on nightly, #[macro_pub] requires #![cfg_attr(doc, feature(decl_macro, rustc_attrs))] and instead emits

#[cfg(doc)]
#[rustc_macro_transparency = "semitransparent"]
pub macro my_macro {
    () => {},
}
#[cfg(not(doc))]
// the previous expansion

This uses the unstable "macros 2.0" to define a macro with the legacy macro_rules! hygeine rules that obeys normal scoping rules and is documented cleanly by rustdoc.

macro_pub automatically sniffs the rustc you're using to compile and determines if it can use decl_macro and rustc_attrs in this way. When these features inevitably get changed, macro_pub will automatically fall back to the stable solution. Additionally, if/when a direct solution to this problem is stabilized (e.g. pub macro_rules!, which has been discussed to do almost exactly what this crate does), macro_pub will be updated to take advantage of that on compatible rustc versions.

Examples

In a module with pub(crate) visibility:

#[macro_use]
extern crate macro_pub;

mod test {
    #[macro_pub(crate)]
    macro_rules! m {
        () => {};
    }
}

test::m!();

With pub(self) visibility, it can't be accessed outside the module:

#[macro_use]
extern crate macro_pub;
# fn main() {}

mod test {
    #[macro_pub(self)]
    macro_rules! m {
        () => {};
    }
}

test::m!(); //~ ERROR

Dependencies

~96KB