Auto merge of #28351 - jonas-schievink:macro-bt, r=nrc

The second commit in this PR will stop printing the macro definition site in backtraces, which cuts their length in half and increases readability (the definition site was only correct for local macros).

The third commit will not print an invocation if the last one printed occurred at the same place (span). This will make backtraces caused by a self-recursive macro much shorter.

(A possible alternative would be to capture the backtrace first, then limit it to a few frames at the start and end of the chain and print `...` inbetween. This would also work with multiple macros calling each other, which is not addressed by this PR - although the backtrace will still be halved)

Example:
```rust
macro_rules! m {
 ( 0 $($t:tt)* ) => ( m!($($t)*); );
 () => ( fn main() {0} );
}

m!(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0);
```

On a semi-recent nightly, this yields:
```
test.rs:3:21: 3:22 error: mismatched types:
 expected `()`,
    found `_`
(expected (),
    found integral variable) [E0308]
test.rs:3  () => ( fn main() {0} );
                              ^
test.rs:1:1: 4:2 note: in expansion of m!
test.rs:2:23: 2:34 note: expansion site
test.rs:1:1: 4:2 note: in expansion of m!
test.rs:2:23: 2:34 note: expansion site
test.rs:1:1: 4:2 note: in expansion of m!
test.rs:2:23: 2:34 note: expansion site
test.rs:1:1: 4:2 note: in expansion of m!
test.rs:2:23: 2:34 note: expansion site
test.rs:1:1: 4:2 note: in expansion of m!
test.rs:2:23: 2:34 note: expansion site
test.rs:1:1: 4:2 note: in expansion of m!
test.rs:2:23: 2:34 note: expansion site
test.rs:1:1: 4:2 note: in expansion of m!
test.rs:2:23: 2:34 note: expansion site
test.rs:1:1: 4:2 note: in expansion of m!
test.rs:2:23: 2:34 note: expansion site
test.rs:1:1: 4:2 note: in expansion of m!
test.rs:2:23: 2:34 note: expansion site
test.rs:1:1: 4:2 note: in expansion of m!
test.rs:2:23: 2:34 note: expansion site
test.rs:1:1: 4:2 note: in expansion of m!
test.rs:2:23: 2:34 note: expansion site
test.rs:1:1: 4:2 note: in expansion of m!
test.rs:2:23: 2:34 note: expansion site
test.rs:1:1: 4:2 note: in expansion of m!
test.rs:2:23: 2:34 note: expansion site
test.rs:1:1: 4:2 note: in expansion of m!
test.rs:2:23: 2:34 note: expansion site
test.rs:1:1: 4:2 note: in expansion of m!
test.rs:2:23: 2:34 note: expansion site
test.rs:1:1: 4:2 note: in expansion of m!
test.rs:6:1: 6:35 note: expansion site
test.rs:3:21: 3:22 help: run `rustc --explain E0308` to see a detailed explanation
error: aborting due to previous error
```

After this patch:
```
test.rs:3:21: 3:22 error: mismatched types:
 expected `()`,
    found `_`
(expected (),
    found integral variable) [E0308]
test.rs:3  () => ( fn main() {0} );
                              ^
test.rs:2:23: 2:34 note: in this expansion of m!
test.rs:6:1: 6:35 note: in this expansion of m!
test.rs:3:21: 3:22 help: run `rustc --explain E0308` to see a detailed explanation
error: aborting due to previous error
```
This commit is contained in:
bors 2015-09-15 03:21:30 +00:00
commit f3e6d31538
6 changed files with 56 additions and 38 deletions

View file

@ -727,30 +727,45 @@ impl EmitterWriter {
cm: &codemap::CodeMap, cm: &codemap::CodeMap,
sp: Span) sp: Span)
-> io::Result<()> { -> io::Result<()> {
let cs = try!(cm.with_expn_info(sp.expn_id, |expn_info| -> io::Result<_> { let mut last_span = codemap::DUMMY_SP;
match expn_info { let mut sp_opt = Some(sp);
Some(ei) => {
let ss = ei.callee.span.map_or(String::new(), while let Some(sp) = sp_opt {
|span| cm.span_to_string(span)); sp_opt = try!(cm.with_expn_info(sp.expn_id, |expn_info| -> io::Result<_> {
let (pre, post) = match ei.callee.format { match expn_info {
codemap::MacroAttribute(..) => ("#[", "]"), Some(ei) => {
codemap::MacroBang(..) => ("", "!"), let (pre, post) = match ei.callee.format {
codemap::CompilerExpansion(..) => ("", ""), codemap::MacroAttribute(..) => ("#[", "]"),
}; codemap::MacroBang(..) => ("", "!"),
try!(self.print_diagnostic(&ss, Note, codemap::CompilerExpansion(..) => ("", ""),
&format!("in expansion of {}{}{}", };
pre, // Don't print recursive invocations
ei.callee.name(), if ei.call_site != last_span {
post), last_span = ei.call_site;
None));
let ss = cm.span_to_string(ei.call_site); let mut diag_string = format!("in this expansion of {}{}{}",
try!(self.print_diagnostic(&ss, Note, "expansion site", None)); pre,
Ok(Some(ei.call_site)) ei.callee.name(),
post);
if let Some(def_site_span) = ei.callee.span {
diag_string.push_str(&format!(" (defined in {})",
cm.span_to_filename(def_site_span)));
}
try!(self.print_diagnostic(&cm.span_to_string(ei.call_site),
Note,
&diag_string,
None));
}
Ok(Some(ei.call_site))
}
None => Ok(None)
} }
None => Ok(None) }));
} }
}));
cs.map_or(Ok(()), |call_site| self.print_macro_backtrace(cm, call_site)) Ok(())
} }
} }

View file

@ -10,7 +10,7 @@
// Test that we get an expansion stack for `for` loops. // Test that we get an expansion stack for `for` loops.
// error-pattern:in expansion of for loop expansion // error-pattern:in this expansion of for loop expansion
fn main() { fn main() {
for t in &foo { for t in &foo {

View file

@ -10,25 +10,25 @@
// Macros in statement vs expression position handle backtraces differently. // Macros in statement vs expression position handle backtraces differently.
macro_rules! fake_method_stmt { //~ NOTE in expansion of macro_rules! fake_method_stmt {
() => { () => {
1.fake() //~ ERROR no method named `fake` found 1.fake() //~ ERROR no method named `fake` found
} }
} }
macro_rules! fake_field_stmt { //~ NOTE in expansion of macro_rules! fake_field_stmt {
() => { () => {
1.fake //~ ERROR no field with that name 1.fake //~ ERROR no field with that name
} }
} }
macro_rules! fake_anon_field_stmt { //~ NOTE in expansion of macro_rules! fake_anon_field_stmt {
() => { () => {
(1).0 //~ ERROR type was not a tuple (1).0 //~ ERROR type was not a tuple
} }
} }
macro_rules! fake_method_expr { //~ NOTE in expansion of macro_rules! fake_method_expr {
() => { () => {
1.fake() //~ ERROR no method named `fake` found 1.fake() //~ ERROR no method named `fake` found
} }
@ -47,11 +47,13 @@ macro_rules! fake_anon_field_expr {
} }
fn main() { fn main() {
fake_method_stmt!(); //~ NOTE expansion site fake_method_stmt!(); //~ NOTE in this expansion of
fake_field_stmt!(); //~ NOTE expansion site fake_field_stmt!(); //~ NOTE in this expansion of
fake_anon_field_stmt!(); //~ NOTE expansion site fake_anon_field_stmt!(); //~ NOTE in this expansion of
let _ = fake_method_expr!(); //~ NOTE expansion site let _ = fake_method_expr!(); //~ NOTE in this expansion of
let _ = fake_field_expr!(); //~ ERROR no field with that name let _ = fake_field_expr!(); //~ ERROR no field with that name
//~^ NOTE in this expansion of
let _ = fake_anon_field_expr!(); //~ ERROR type was not a tuple let _ = fake_anon_field_expr!(); //~ ERROR type was not a tuple
//~^ NOTE in this expansion of
} }

View file

@ -19,11 +19,11 @@ macro_rules! call_nested_expr {
() => (nested_expr!()) () => (nested_expr!())
} }
macro_rules! call_nested_expr_sum { //~ NOTE in expansion of macro_rules! call_nested_expr_sum {
() => { 1 + nested_expr!(); } //~ ERROR unresolved name () => { 1 + nested_expr!(); } //~ ERROR unresolved name
} }
fn main() { fn main() {
1 + call_nested_expr!(); //~ ERROR unresolved name 1 + call_nested_expr!(); //~ ERROR unresolved name
call_nested_expr_sum!(); //~ NOTE expansion site call_nested_expr_sum!(); //~ NOTE in this expansion of
} }

View file

@ -16,14 +16,15 @@
fn print(_args: std::fmt::Arguments) {} fn print(_args: std::fmt::Arguments) {}
macro_rules! myprint { //~ NOTE in expansion of macro_rules! myprint {
($($arg:tt)*) => (print(format_args!($($arg)*))); ($($arg:tt)*) => (print(format_args!($($arg)*))); //~ NOTE in this expansion of
} }
macro_rules! myprintln { //~ NOTE in expansion of macro_rules! myprintln {
($fmt:expr) => (myprint!(concat!($fmt, "\n"))); //~ ERROR invalid reference to argument `0` ($fmt:expr) => (myprint!(concat!($fmt, "\n"))); //~ ERROR invalid reference to argument `0`
//~^ NOTE in this expansion of
} }
fn main() { fn main() {
myprintln!("{}"); //~ NOTE expansion site myprintln!("{}"); //~ NOTE in this expansion of
} }

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// forbid-output: in expansion of // forbid-output: in this expansion of
macro_rules! make_method { macro_rules! make_method {
($name:ident) => ( fn $name(&self) { } ) ($name:ident) => ( fn $name(&self) { } )