/expandable

What if we could check declarative macros before using them?

Primary LanguageRust

expandable

An attribute-macro based macro_rules! expansion checker.


Sylvester Stallone in Rambo: First Blood Part II. The image has been edited such that his two hands are thumbsup-ing. His body is covered with dust and sweat and a bit of blood (not his). At the bottom of the image is written 'POV: #[expandable::expr] stops complaining'.

Textbook example

rustc treats macro definitions as some opaque piece of tokens and don't do any check on them. For instance, the following macro definition is valid:

macro_rules! js_concat {
    ($left:expr, $right:expr) => {
        $left ++ $right
    };
}

However, any call to the js_concat macro is invalid, as the ++ operator does not exist in Rust. Luckily for us, this crate provides the expandable::expr macro, that checks that the macro expands to a valid expression. Let's use it on js_concat:

#[expandable::expr]
macro_rules! js_concat {
    ($left:expr, $right:expr) => {
        $left ++ $right
    };
}

This emits the following error 1:

error: Potentially invalid expansion. Expected an identifier.
 --> tests/ui/fail/js_concat.rs:4:16
  |
4 |         $left ++ $right
  |                ^

Expansion context

Macros can expand to different things depending on where they are called. As a result, expandable must know what the macro expands to. To do so, multiple macros are available:

  • Macros that expand to expressions are checked by expandable::expr,
  • Macros that expand to items are checked by expandable::item,
  • TODO: pattern, statements, type.

Minimal Supported Rust Version (MSRV), syntax support and stability

expandable supports Rust 1.56 and above. Bumping the MSRV is considered a breaking change.

Note that the embedded parser will support syntax that was introduced after Rust 1.56.

Adding support for newer syntax is not considered a breaking change.

The error messages generated by expandable are not stable and may change without notice.

Any change that may trigger errors on previously accepted code is considered a breaking change.

Footnotes

  1. The Rust grammar is not fully implemented at the moment, leading to incomplete "expected xxx" list this will be fixed before the first non-alpha release of this crate.