Llogiq on stuff

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 Exprs or Items.

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.