Propagate macro backtraces more often, improve formatting diagnostics

* In noop_fold_expr, call new_span in these cases:
    - ExprMethodCall's identifier
    - ExprField's identifier
    - ExprTupField's integer

   Calling new_span for ExprMethodCall's identifier is necessary to print
   an acceptable diagnostic for write!(&2, ""). We see this error:

       <std macros>:2:20: 2:66 error: type `&mut _` does not implement any method in scope named `write_fmt`
       <std macros>:2 ( & mut * $ dst ) . write_fmt ( format_args ! ( $ ( $ arg ) * ) ) )
                                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

   With this change, we also see a macro expansion backtrace leading to
   the write!(&2, "") call site.

 * After fully expanding a macro, we replace the expansion expression's
   span with the original span. Call fld.new_span to add a backtrace to
   this span. (Note that I'm call new_span after bt.pop(), so the macro
   just expanded isn't on the backtrace.)

   The motivating example for this change is println!("{}"). The format
   string literal is concat!($fmt, "arg") and is inside the libstd macro.
   We need to see the backtrace to find the println! call site.

 * Add a backtrace to the format_args! format expression span.

Addresses #23459
This commit is contained in:
Ryan Prichard 2015-04-03 01:27:04 -07:00
parent 6790b0e519
commit 0f46e4f1f2
3 changed files with 12 additions and 6 deletions

View File

@ -55,7 +55,7 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
fully_expanded.map(|e| ast::Expr {
id: ast::DUMMY_NODE_ID,
node: e.node,
span: span,
span: fld.new_span(span),
})
}

View File

@ -17,6 +17,7 @@ use ext::base::*;
use ext::base;
use ext::build::AstBuilder;
use fmt_macros as parse;
use fold::Folder;
use parse::token::special_idents;
use parse::token;
use ptr::P;
@ -649,6 +650,10 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
names: HashMap<String, P<ast::Expr>>)
-> P<ast::Expr> {
let arg_types: Vec<_> = (0..args.len()).map(|_| None).collect();
// Expand the format literal so that efmt.span will have a backtrace. This
// is essential for locating a bug when the format literal is generated in
// a macro. (e.g. println!("{}"), which uses concat!($fmt, "\n")).
let efmt = ecx.expander().fold_expr(efmt);
let mut cx = Context {
ecx: ecx,
args: args,
@ -663,9 +668,8 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
pieces: Vec::new(),
str_pieces: Vec::new(),
all_pieces_simple: true,
fmtsp: sp,
fmtsp: efmt.span,
};
cx.fmtsp = efmt.span;
let fmt = match expr_to_string(cx.ecx,
efmt,
"format argument must be a string literal.") {

View File

@ -1176,7 +1176,7 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span}: Expr, folder: &mut T) ->
}
ExprMethodCall(i, tps, args) => {
ExprMethodCall(
respan(i.span, folder.fold_ident(i.node)),
respan(folder.new_span(i.span), folder.fold_ident(i.node)),
tps.move_map(|x| folder.fold_ty(x)),
args.move_map(|x| folder.fold_expr(x)))
}
@ -1246,11 +1246,13 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span}: Expr, folder: &mut T) ->
}
ExprField(el, ident) => {
ExprField(folder.fold_expr(el),
respan(ident.span, folder.fold_ident(ident.node)))
respan(folder.new_span(ident.span),
folder.fold_ident(ident.node)))
}
ExprTupField(el, ident) => {
ExprTupField(folder.fold_expr(el),
respan(ident.span, folder.fold_usize(ident.node)))
respan(folder.new_span(ident.span),
folder.fold_usize(ident.node)))
}
ExprIndex(el, er) => {
ExprIndex(folder.fold_expr(el), folder.fold_expr(er))