Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to correctly deal with spans in procedural macros?

Tags:

plugins

rust

I want my procedural macro to replace some BinaryOps with methods. How do I set the spans so that if errors occur my users are not completely confused?

like image 533
llogiq Avatar asked Nov 08 '22 14:11

llogiq


1 Answers

After some looking into rustc's source, I came to the conclusion that following the "expansion" model produces the best results. So we keep the original Span but for the expn_id, which we can get by calling ExtCtxt::backtrace().

It appears to be a good idea to set this in both cases outlined in the question. The operator can be seen as expanded into the (function call) path, and the original binary operation expression as expanded into the function call. In code:

match expr.unwrap() {
    ..
    Expr { node: ExprKind::Binary( Spanned { node: Add, span: op }, l, r), span, .. } => {
        let bt = self.cx.backtrace(); // get the expansion ID
        let path = self.cx.path(Span { expn_id: bt, ..op }, vec![crate_name, trait_name, fn_name]);
        let epath = self.cx.expr_path(path); // path expression
        let args_expanded = self.fold_exprs(args);
        self.cx.expr_call(Span { expn_id: bt, ..span }, epath, args_expanded)
        // ^ outer expression
    }
    ..
}
like image 50
llogiq Avatar answered Jan 04 '23 01:01

llogiq