Procedural Macros vs. Macros
I’m using rustc 1.11.0-nightly (915b003e3 2016-06-02)
for this post
Starting not one, but two projects revolving around procedural macros is a great way to learn a thing or two about them. One thing that bugged me was that they didn’t work easily when the code that should be manipulated involved macros. Luckily, stackoverflow user ker helped me out, and I wanted to share the technique here.
The idea is that we don’t care about macros – we do care about the expanded
code coming out of it. Macros we care about can appear in Item
position and
in Expr
position. For both, we want to expand the macros and apply whatever
transformation we implement on the resulting Expr
s or Item
s.
Without further ado, here’s the code:
impl<'a, 'cx> Folder for Overflower<'a, 'cx> {
fn fold_item(&mut self, item: P<Item>) -> SmallVector<P<Item>> {
if let ItemKind::Mac(_) = item.node {
let expanded = expand_item(item, &mut self.cx.expander());
expanded.into_iter()
.flat_map(|i| self.fold_item(i).into_iter())
.collect()
} else {
fold::noop_fold_item(item, self)
}
}
fn fold_expr(&mut self, expr: P<Expr>) -> P<Expr> {
if self.mode == Mode::DontCare { return expr; }
if let ExprKind::Mac(_) = expr.node {
let expanded = expand_expr(expr.unwrap(), &mut self.cx.expander());
self.fold_expr(expanded)
} else {
..
}
}
..
}
Of course, if we’d like to change items, we’d use our own code instead of
fold::noop_fold_item(..)
. In other news, overflower is now beginning to be
somewhat usable – I’ll need to add a bit of documentation, but the code itself
seems to work pretty well.