restricted crate (prudent-rs/restricted) prevents
consumers of your Rust macros from
- accidental, or
- intentional (well-intended or malicious)
unguarded, or corrupt access to your macro's internals. It enables your crates to have private-like variables, functions and types, shared by multiple macros.
That is unlike standard items and variables defined by macros, which, if hygienic, are visible only to the macro that defined them, but not to any successive invocations of macros from the same crate.
restricted makes these variables, functions and types visible also to your other macros,
invoked after the invocation that generates them.
restricted crate provides macros that allow you, a macro author, to prevent consumers of your
macros from accessing to your declaration
statements (that is:
items and variables, even if declared with
Span::call_site()).
Macros generate code that blends, or is injected, into the invocator's source code. But
macro_rules!are hygienic aboutlet/let mut(and'labelsand$crate). That is good for isolation of local variables. However, it doesn't allow your other macro(s) to access the same variables again.macro_rules!are non-hygienic about any other items they define. Consumers of these macros can access to your macro's internals!- proc macros can
- apply
Span::mixed_site()to act just likemacro_rules! - generate other items (and hence any items) as hygienic with
Span::call_site() - generate any items as non-hygienic with
Span::def_site(), but onnightlyRust toolchain only.
- apply
restricted generates
- random-based names for your macro's items and variables, and
- accessor macros that can be invoked only from your macro(s) and not by the consumer code (by
validating the required
Span).
restricted provides macros for
- declaration of (local)
letandlet mutvariables - definition of
staticandconstitems- and both of the above create local macros to access those variables or
constorstaticitems
- and both of the above create local macros to access those variables or
- local re-export of private items (from a submodule) and
- macros to access those re-exports by specifying the item's "short name", and
- "direct" local macros to access those re-exports (one macro per item).
There are two reason why restricted crate supports simple syntax only:
-
Supporting full grammar of
fn,type, orstruct/enum/unionorimpldefinitions (with generics and bounds), would be complicated. Invocations of such macros would be difficult to write. Difficult even to read, or to search in.And, migrating existing code would involve extra work.
-
A macro can return a name/path of
fn,typeandstruct/enum/unionidentifiers. Invocation of such a macro works in Rust in place of the identifier. (In most use cases it works out of the box, and in some it has easy workarounds, like putting the invocation between<...>). For example:macro_rules! def_struct_s { () => { struct S {} impl S { const fn new() -> Self { Self {} } } }; } def_struct_s!(); // accessor macro. This is a simplified example that does not perform Span validation. // `restricted` implements the `Span` validation by forwarding to a procedural macro. macro_rules! StructS { ($token_carrier: tt) => { S }; } const _: StructS!(.) = <StructS!(.)>::new(); pub fn take_value(_: StructS!(.)) {} pub fn take_ref(_: &StructS!(.)) {} pub fn return_value() -> StructS!(.) { todo!() } type StructAlias = StructS!(.); const _: StructS!(.) = StructAlias {};
However, there is no way to apply the same to
traititems. For those we have a workaround:use_with.
That's aligned with how const and static work already. They allow only one identifier per each
const or static definition. Rust refuses definitions like:
const (_FIRST_CONST,_SECOND_CONST) : (bool, u8) = (true, 1);
static (_FIRST_STATIC, _SECOND_STATIC) : (bool, u8) = (true, 1);Any let and let mut variables generated by macro_rules (and by proc macros with
Span::mixed_site() are hygienic, so they can't be accessed from outside (of the macro that
created them).
However, such let and let mut variables then can't be accessed from other macro(s), even if
those other macro(s) are from the same crate (the crate which created the macro that created the
variable).
Please give thumbs up (and contribute, if you can) to
- SergioBenitez/proc-macro2-diagnostics#13 defect: Error message and details missing, when macro fails to generate main() on STABLE
- SergioBenitez/proc-macro2-diagnostics#12 tests/stable/errors.rs fails with Rust 1.89.0 and also stable 1.95.0
Source file paths have be in UTF-8.
If you use an unicameral alphabet (which doesn't differentiate between lowercase and uppercase), you
may need to indicate the naming convention (one of: CamelCase, lower_case or UPPER_CASE).
That's because (obviously) restricted then can't detect the naming convention. restricted needs
the convention choice for generating the random part (which is always in ASCII, to make it easier to
maintain).
You can include unicameral letters and use auto-detection if use also include some bicameral letters and/or an underscore, and if those clearly imply the convention.
NOT compatible with dtolnay/watt (because of side effects of
build.rs).