rustrust-compiler-plugin

How to correctly deal with spans in procedural macros?


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?


Solution

  • 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
        }
        ..
    }